Refresher (1) As a refresher to statistical plots, let’s build a scatter plot with an additional statistic layer.

A dataset called movies_small is coded in your workspace. It is a random sample of 1000 observations from the larger movies dataset, that’s inside the ggplot2movies package. The dataset contains information on movies from IMDB. The variable votes is the number of IMDB users who have rated a movie and the rating (converted into a categorical variable) is the average rating for the movie.

# Create movies_small
library(ggplot2movies)
library(ggplot2)
set.seed(123)
movies_small <- movies[sample(nrow(movies), 1000), ]
movies_small$rating <- factor(round(movies_small$rating))
# Explore movies_small with str()
str(movies_small)
Classes 'tbl_df', 'tbl' and 'data.frame':   1000 obs. of  24 variables:
 $ title      : chr  "Fair and Worm-er" "Shelf Life" "House: After Five Years of Living" "Three Long Years" ...
 $ year       : int  1946 2000 1955 2003 1963 1992 1999 1972 1994 1985 ...
 $ length     : int  7 4 11 76 103 107 87 84 127 94 ...
 $ budget     : int  NA NA NA NA NA NA NA NA NA NA ...
 $ rating     : Factor w/ 10 levels "1","2","3","4",..: 7 7 6 8 8 5 4 8 5 5 ...
 $ votes      : int  16 11 15 11 103 28 105 9 37 28 ...
 $ r1         : num  0 0 14.5 4.5 4.5 4.5 14.5 0 4.5 4.5 ...
 $ r2         : num  0 0 0 0 4.5 0 4.5 0 4.5 0 ...
 $ r3         : num  0 0 4.5 4.5 0 4.5 4.5 0 14.5 4.5 ...
 $ r4         : num  0 0 4.5 0 4.5 4.5 4.5 0 4.5 14.5 ...
 $ r5         : num  4.5 4.5 0 0 4.5 0 4.5 14.5 24.5 4.5 ...
 $ r6         : num  4.5 24.5 34.5 4.5 4.5 0 14.5 0 4.5 14.5 ...
 $ r7         : num  64.5 4.5 24.5 0 14.5 4.5 14.5 14.5 14.5 14.5 ...
 $ r8         : num  14.5 24.5 4.5 4.5 14.5 24.5 14.5 24.5 14.5 14.5 ...
 $ r9         : num  0 0 0 14.5 14.5 24.5 14.5 14.5 4.5 4.5 ...
 $ r10        : num  14.5 24.5 14.5 44.5 44.5 24.5 14.5 44.5 4.5 24.5 ...
 $ mpaa       : chr  "" "" "" "" ...
 $ Action     : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Animation  : int  1 0 0 0 0 0 0 0 0 0 ...
 $ Comedy     : int  1 0 0 1 0 1 1 1 0 0 ...
 $ Drama      : int  0 0 0 0 1 0 0 0 1 1 ...
 $ Documentary: int  0 0 1 0 0 0 0 0 0 0 ...
 $ Romance    : int  0 0 0 0 0 0 1 0 0 0 ...
 $ Short      : int  1 1 1 0 0 0 0 0 0 0 ...
# Build a scatter plot with mean and 95% CI
ggplot(movies_small, aes(x = rating, y = votes)) +
  geom_point() +
  stat_summary(fun.data = "mean_cl_normal",
               geom = "crossbar",
               width = 0.2,
               col = "red") +
  scale_y_log10()

Refresher (2) The plot in the graphics device is a variation on an oft-seen ggplot2 example using the diamonds dataset (containing information on several variables of over 50,000 diamonds).

Recall that there are a variety of scale_ functions. Here, data are transformed or filtered first, after which the plot and associated statistics are computed. For example, scale_y_continuous(limits = c(100, 1000) will remove values outside that range.

Contrast this to coord_cartesian(), which computes the statistics before plotting. That means that the plot and summary statistics are performed on the raw data. That’s why we say that coord_cartesian(c(100, 1000)) “zooms in” a plot. This was discussed in the chapter on coordinates in course 2.

Here we’re going to expand on this and introduce scale_x_log10() and scale_y_log10() which perform log10 transformations, and coord_equal(), which sets an aspect ratio of 1 (coord_fixed() is also an option).

Your task is to reproduce the plot in the viewer. Before you do this, it might be a good idea to explore diamonds in the console if you are not familiar with it.

# Reproduce the plot
ggplot(diamonds, aes(x = carat, y = price, col = color)) +
  geom_point(alpha = 0.5, size = 0.5, shape = 16) +
  scale_x_log10(expression(log[10](Carat)), limits = c(0.1,10)) +
  scale_y_log10(expression(log[10](Price)), limits = c(100,100000)) +
  scale_color_brewer(palette = "YlOrRd") +
  coord_equal() +
  theme_classic()

Refresher (3) The goal plot from the previous exercise is coded in your editor. Here you’ll expand on this plot with stat_smooth() model instead of showing every data point.

# Add smooth layer and facet the plot
ggplot(diamonds, aes(x = carat, y = price, col = color)) +
  stat_smooth(method = "lm") +
  scale_x_log10(expression(log[10](Carat)), limits = c(0.1,10)) +
  scale_y_log10(expression(log[10](Price)), limits = c(100,100000)) +
  scale_color_brewer(palette = "YlOrRd") +
  coord_equal() +
  theme_classic()

Transformations In this exercise you’ll return to the first plotting exercise and see how box plots compare to dot plots for representing high-density data.

Box plots are very useful, but they don’t solve all your problems all the time, for example, when your data are heavily skewed, you will still need to transform it. You’ll see that here, using the movies_small dataset, a subset of 10,000 observations of ggplot2movies::movies.

# movies_small is available
# Add a boxplot geom
d <- ggplot(movies_small, aes(x = rating, y = votes)) +
  geom_point() +
  geom_boxplot() +
  stat_summary(fun.data = "mean_cl_normal",
               geom = "crossbar",
               width = 0.2,
               col = "red")
# Untransformed plot
d

# Transform the scale
d + scale_y_log10()

# Transform the coordinates
d + coord_trans(y = "log10")
Error in if (zero_range(range)) { : missing value where TRUE/FALSE needed

Cut it up! If you only have continuous variables, you can convert them into ordinal variables using any of the following functions:

cut_interval(x, n) makes n groups from vector x with equal range. cut_number(x, n) makes n groups from vector x with (approximately) equal numbers of observations. cut_width(x, width) makes groups of width width from vector x. This is useful when you want to summarize a complex scatter plot like the one shown in the viewer. By applying these functions to the carat variable and mapping that onto the group aesthetic, you can convert the scatter plot in the viewer into a series of box plots on the fly.

# Plot object p
p <- ggplot(diamonds, aes(x = carat, y = price))
# Use cut_interval
p + geom_boxplot(aes(group = cut_interval(carat, n=10)))

# Use cut_number
p + geom_boxplot(aes(group = cut_number(carat, n=10)))

# Use cut_width
p + geom_boxplot(aes(group = cut_width(carat, width = 0.25)))

geom_density() To make a straightforward density plot, add a geom_density() layer.

Before plotting, you will calculate the emperical density function, similar to how you can use the density() function in the stats package, available by default when you start R. The following default parameters are used (you can specify these arguments both in density() as well as geom_density()):

bw = “nrd0”, telling R which rule to use to choose an appropriate bandwidth. kernel = “gaussian”, telling R to use the Gaussian kernel. We’ve already prepared a data frame test_data for you, containing three columns: norm, bimodal and uniform. Each column represents 200 samples from a normal, bimodal and uniform distribution.

rn <- rnorm(200, 0, 1)
bimodalDistFunc <- function (n,cpct, mu1, mu2, sig1, sig2) {
  y0 <- rlnorm(n,mean=mu1, sd = sig1)
  y1 <- rlnorm(n,mean=mu2, sd = sig2)
  flag <- rbinom(n,size=1,prob=cpct)
  y <- y0*(1 - flag) + y1*flag 
}
bm <- bimodalDistFunc(n=200,0.4,-1,1, 1,1)
ud <- runif(200, -2, 1)
test_data <- data.frame("norm" = rn,
                        "bimodal" = bm,
                        "uniform" = ud)
head(test_data)
# test_data is available
# Calculating density: d
d <- density(test_data$norm)
# Use which.max() to calculate mode
mode <- d$x[which.max(d$y)]
# Finish the ggplot call
ggplot(test_data, aes(x = norm)) +
  geom_rug() +
  geom_density() +
  geom_vline(xintercept = mode, col = "red")

Combine density plots and histogram Sometimes it is useful to compare a histogram with a density plot. However, the histogram’s y-scale must first be converted to frequency instead of absolute count. After doing so, you can add an empirical PDF using geom_density() or a theoretical PDF using stat_function().

Can you finish the plot below by following the steps?

# test_data is available
# Arguments you'll need later on
fun_args <- list(mean = mean(test_data$norm), sd = sd(test_data$norm))
# Finish the ggplot
ggplot(test_data, aes(x = norm)) +
geom_histogram(aes(y=..density..))+
geom_density(col = "red") +
stat_function(fun = dnorm, args = fun_args, col="blue")

Adjusting density plots There are three parameters that you may be tempted to adjust in a density plot:

bw - the smoothing bandwidth to be used, see ?density for details adjust - adjustment of the bandwidth, see density for details kernel - kernel used for density estimation, defined as “g” = gaussian “r” = rectangular “t” = triangular “e” = epanechnikov “b” = biweight “c” = cosine “o” = optcosine In this exercise you’ll use a dataset containing only four points, small_data, so that you can see how these three arguments affect the shape of the density plot.

The vector get_bw contains the bandwidth that is used by default in geom_density(). p is a basic plotting object that you can start from.

# small_data is available
small_data <- data.frame("x" = c(-3.5, 0.0,0.5, 6.0))
# Get the bandwith
get_bw <- density(small_data$x)$bw
# Basic plotting object
p <- ggplot(small_data, aes(x = x)) +
  geom_rug() +
  coord_cartesian(ylim = c(0,0.5))
# Create three plots
p + geom_density()

p + geom_density(adjust = 0.25)

p + geom_density(bw = 0.25 * get_bw)

# Create two plots
p + geom_density(kernel = "r")

p + geom_density(kernel = "e")

Box plots with varying width A drawback of showing a box plot per group, is that you don’t have any indication of the sample size, n, in each group, that went into making the plot. One way of dealing with this is to use a variable width for the box, which reflects differences in n.

Can you add some good-looking box plots to the basic plot coded on the right?

# Finish the plot
ggplot(diamonds, aes(x = cut, y = price, col = color)) +
  geom_boxplot(varwidth = TRUE) +
  facet_grid(. ~ color)

Mulitple density plots In this exercise you’ll combine multiple density plots. Here, you’ll combine just two distributions, a normal and a bimodal.

The first thing to remember is that you can consider values as two separate variables, like in the test_data data frame, or as a single continuous variable with their ID as a separate categorical variable, like in the test_data2 data frame. test_data2 is more convenient for combining and comparing multiple distributions.

test_data2 <- data.frame("dist" = c(rep("norm", 200), rep("bimodal", 200)),
                         "value" = c(test_data$norm, test_data$bimodal))
# test_data and test_data2 are available
str(test_data)
'data.frame':   200 obs. of  3 variables:
 $ norm   : num  -0.602 -0.994 1.027 0.751 -1.509 ...
 $ bimodal: num  0.986 1.232 3.668 0.414 0.094 ...
 $ uniform: num  0.197 -0.171 -1.327 0.749 0.408 ...
str(test_data2)
'data.frame':   400 obs. of  2 variables:
 $ dist : Factor w/ 2 levels "bimodal","norm": 2 2 2 2 2 2 2 2 2 2 ...
 $ value: num  -0.602 -0.994 1.027 0.751 -1.509 ...
# Plot with test_data
ggplot(test_data, aes(x = norm)) +
  geom_rug()+
  geom_density()

# Plot two distributions with test_data2
ggplot(test_data2, aes(x = value, fill = dist, col = dist)) +
  geom_rug(alpha = 0.6) +
  geom_density(alpha = 0.6)

Multiple density plots (2) When you looked at multiple box plots, you compared the total sleep time of various mammals, sorted according to their eating habits. One thing you noted is that for insectivores, box plots didn’t really make sense, since there were only 5 observations to begin with. You decided that you could nonetheless use the width of a box plot to show the difference in sample size between the groups. Here, you’ll see a similar thing with density plots.

A cleaned up version of the mammalian dataset is available as mammals.

head(msleep)
mammals <- msleep[,c("vore","sleep_total")]
mammals
# Individual densities
ggplot(mammals[mammals$vore == "Insecti", ], aes(x = sleep_total, fill = vore)) +
  geom_density(col = NA, alpha = 0.35) +
  scale_x_continuous(limits = c(0, 24)) +
  coord_cartesian(ylim = c(0, 0.3))

# With faceting
ggplot(mammals, aes(x = sleep_total, fill = vore)) +
  geom_density(col = NA, alpha = 0.35) +
  scale_x_continuous(limits = c(0, 24)) +
  coord_cartesian(ylim = c(0, 0.3)) +
  facet_wrap( ~ vore, nrow = 2)

# Note that by default, the x ranges fill the scale
ggplot(mammals, aes(x = sleep_total, fill = vore)) +
  geom_density(col = NA, alpha = 0.35) +
  scale_x_continuous(limits = c(0, 24)) +
  coord_cartesian(ylim = c(0, 0.3))

# Trim each density plot individually
ggplot(mammals, aes(x = sleep_total, fill = vore)) +
  geom_density(col = NA, alpha = 0.35, trim = TRUE) +
  scale_x_continuous(limits=c(0,24)) +
  coord_cartesian(ylim = c(0, 0.3))

Weighted density plots When plotting a single variable, the density plots (and their bandwidths) are calculated separate for each variable (see the plot from the previous exercise, provided).

However, when you compare several variables (such as eating habits) it’s useful to see the density of each subset in relation to the whole data set. This holds true for multiple density plots as well as for violin plots.

For this, we need to weight the density plots so that they’re relative to each other. Each density plot is adjusted according to what proportion of the total data set each sub-group represents. We calculated this using the dplyr commands on lines 11-15.

The mammals data frame is available as before. After executing the commnads, it will have the variable n, which we’ll use for weighting.

# Unweighted density plot from before
ggplot(mammals, aes(x = sleep_total, fill = vore)) +
  geom_density(col = NA, alpha = 0.35) +
  scale_x_continuous(limits = c(0, 24)) +
  coord_cartesian(ylim = c(0, 0.3))

# Unweighted violin plot
ggplot(mammals, aes(x = vore, y = sleep_total, fill = vore)) +
  geom_violin()

# Calculate weighting measure
library(dplyr)

Attaching package: 'dplyr'

The following objects are masked from 'package:stats':

    filter, lag

The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
mammals2 <- mammals %>%
  group_by(vore) %>%
  mutate(n = n() / nrow(mammals)) -> mammals
# Weighted density plot
ggplot(mammals, aes(x = sleep_total, fill = vore)) +
  geom_density(aes(weight = n), col = NA, alpha = 0.35) +
  scale_x_continuous(limits = c(0, 24)) +
  coord_cartesian(ylim = c(0, 0.3))

# Weighted violin plot
ggplot(mammals, aes(x = vore, y = sleep_total, fill = vore)) +
  geom_violin(aes(weight = n), col = NA)

2D density plots (1) You can consider two orthogonal density plots in the form of a 2D density plot. Just like with a 1D density plot, you can adjust the bandwidth of both axes independently.

The data is stored in the faithful data frame, available in the datasets package. The object p contains the base definitions of a plot.

# Base layers
p <- ggplot(faithful, aes(x = waiting, y = eruptions)) +
  scale_y_continuous(limits = c(1, 5.5), expand = c(0, 0)) +
  scale_x_continuous(limits = c(40, 100), expand = c(0, 0)) +
  coord_fixed(60 / 4.5)
# 1 - Use geom_density_2d()
p + geom_density_2d()

# 2 - Use stat_density_2d() with arguments
p + stat_density_2d(aes(col = ..level..), h = c(5, 0.5))

2D density plots (2) Continuing with the density plots from the last exercise, here you’ll explore the viridis package. This package contains multi-hue color palettes suitable for continuous variables.

The advantage of these scales is that instead of providing an even color gradient for a continuous scale, they highlight the highest values by using an uneven color gradient on purpose. The high values are lighter colors (yellow versus blue), so they stand out more.

A shaded 2D density plot showing the same data as the previous exercise has been provided for you. Up to you to upgrade it!

# Load in the viridis package
library(viridis)
package 'viridis' was built under R version 3.4.4Loading required package: viridisLite
# Add viridis color scale
ggplot(faithful, aes(x = waiting, y = eruptions)) +
  scale_y_continuous(limits = c(1, 5.5), expand = c(0,0)) +
  scale_x_continuous(limits = c(40, 100), expand = c(0,0)) +
  coord_fixed(60/4.5) +
  stat_density_2d(geom = "tile", aes(fill = ..density..), h=c(5,.5), contour = FALSE)+ scale_fill_viridis()

Pair plots and correlation matrices On startup, R features two useful quick-and-dirty pairs plots functions. They both only take continuous variables.

You’ll be working with the iris dataset and with mtcars_fact, a version of mtcars where categorical variables have been converted into actual factor columns.

# pairs
pairs(iris[1:4])

# chart.Correlation
library(PerformanceAnalytics)
chart.Correlation(iris[1:4])

# ggpairs
library(GGally)
ggpairs(iris[1:3])

Create a correlation matrix in ggplot2 Instead of using an off-the-shelf correlation matrix function, you can of course create your own plot. Just for fun, in this exercise, you’ll re-create the scatterplot you see on the right. The strength of the correlation is depicted by the size and color of the points and labels.

For starters, a correlation matrix can be calculated using, for example, cor(dataframe) (if all variables are numerical). Before you can use your data frame to create your own correlation matrix plot, you’ll need to get it in the right format.

In the editor, you can see the definition of cor_list(), a function that re-formats the data frame x. Here, L is used to add the points to the lower triangle of the matrix, and M is used to add the numerical values as text to the upper triangle of the matrix. With reshape2::melt(), the correlation matrices L and M are each converted into a three-column data frame: the x and y axes of the correlation matrix make up the first two columns and the corresponding correlation coefficient makes up the third column. These become the new variables “points” and “labels”, which can be mapped onto the size aesthetic for the points in the lower triangle and onto the label aesthetic for the text in the upper triangle, respectively. Their values will be the same, but their positions on the plot will be symmetrical about the diagonal! Merging L and M, you have everything you need.

If you’re not familiar with reshape2 - don’t worry, the only reason we use that instead of tidyr is that reshape2::melt() can handle a matrix, whereas tidyr::gather() requires a data frame. At this point you just need to understand how to use the output from cor_list().

You’ll first use dplyr to execute this function on the continuous variables in the iris data frame (the first four columns), but separately for each species. Please refer to the course on dplyr if you are not familiar with these functions.

Next, you’ll actually plot the resulting data frame with ggplot2 functions.

library(ggplot2)
library(reshape2)
cor_list <- function(x) {
  L <- M <- cor(x)
  
  M[lower.tri(M, diag = TRUE)] <- NA
  M <- melt(M)
  names(M)[3] <- "points"
  
  L[upper.tri(L, diag = TRUE)] <- NA
  L <- melt(L)
  names(L)[3] <- "labels"
  
  merge(M, L)
}
# Calculate xx with cor_list
library(dplyr)
xx <- iris %>%
  group_by(Species) %>%
  do(cor_list(.[1:4])) 
# Finish the plot
ggplot(xx, aes(x = Var1, y = Var2)) +
  geom_point(aes(col = points, size = abs(points)), shape = 16) +
  geom_text(aes(col = labels,  size = abs(labels), label = round(labels, 2))) +
  scale_size(range = c(0, 6)) +
  scale_color_gradient2("r", limits = c(-1, 1)) +
  scale_y_discrete("", limits = rev(levels(xx$Var1))) +
  scale_x_discrete("") +
  guides(size = FALSE) +
  geom_abline(slope = -1, intercept = nlevels(xx$Var1) + 1) +
  coord_fixed() +
  facet_grid(. ~ Species) +
  theme(axis.text.y = element_text(angle = 45, hjust = 1),
        axis.text.x = element_text(angle = 45, hjust = 1),
        strip.background = element_blank())

Proportional/stacked bar plots Before you head over to ternary plots, let’s try to make a classical proportional/stacked bar plot of a subset of the data. We’ll use a stacked bar plot and the coord_flip() function to flips the x and y axes.

The data frame for the African Soil Profiles Database is available in your workspace as africa and can be found in the GSIF package. It contains three columns: Sand, Silt and Clay. A smaller version, containing only 50 observations is stored in africa_sample.

In the first course we mentioned that in the data layer, the structure of the data should reflect how you wish to plot it. For a ternary plot, you need to have three separate variables, for example, Sand, Silt and Clay in africa. However, for a proportional/stacked bar plot, you just need two. The type should be defined as three levels within a single factor variable. That is, you want tidy data.

It’s also useful to maintain the site IDs as a variable within the data frame, currently, they are stored at row names, which is poor style and not useful.

# Explore africa
str(africa)
'data.frame':   40093 obs. of  3 variables:
 $ Sand: num  24 36 56 52 65 43 42 47 57 51 ...
 $ Silt: num  12 14 18 21 3 14 22 19 15 14 ...
 $ Clay: num  64 50 26 27 32 43 36 34 28 35 ...
africa_sample <- africa[sample(nrow(africa), 50), ]
str(africa_sample)
'data.frame':   50 obs. of  3 variables:
 $ Sand: num  5 58 35 34 42 65 15 89 63 87 ...
 $ Silt: num  17 26 28 10 14 8 43 4 3 6 ...
 $ Clay: num  78 16 37 56 44 27 42 7 34 7 ...
# Add an ID column from the row.names
africa_sample$ID <- row.names(africa_sample)
# Gather africa_sample
library(tidyr)

Attaching package: 'tidyr'

The following object is masked from 'package:GSIF':

    extract

The following object is masked from 'package:reshape2':

    smiths
africa_sample_tidy <- gather(africa_sample, key, value, -ID)
head(africa_sample_tidy)
# Finish the ggplot command
ggplot(africa_sample_tidy, aes(x = factor(ID), y = value, fill = key)) +
  geom_col() +
  coord_flip()

Producing ternary plots Ok, let’s move onto ternary plots. For this you’ll use the ggtern package, which provides the ggtern() function.

In contrast to what you just saw in africa_small_tidy, the three soil properties, Sand, Silt and Clay, are not going to be located in a single variable. The distinction between wide and tidy format data was discussed in the first course and here you’ll see it in action. Sometimes you need to rearrange your data for the desired plot type.

Here, you’ll use the complete dataset, africa, containing three separate variables for the measures of interest: that format is perfect for a ternary plot.

# Load ggtern
library(ggtern)
--
Consider donating at: http://ggtern.com
Even small amounts (say $10-50) are very much appreciated!
Remember to cite, run citation(package = 'ggtern') for further info.
--

Attaching package: 'ggtern'

The following objects are masked from 'package:ggplot2':

    %+%, aes, annotate, calc_element, ggplot, ggplotGrob, ggplot_build, ggplot_gtable, ggsave,
    layer_data, theme, theme_bw, theme_classic, theme_dark, theme_gray, theme_light, theme_linedraw,
    theme_minimal, theme_void
# Build ternary plot
ggtern(africa, aes(x = Sand, y = Silt, z = Clay)) +
  geom_point(shape=16, alpha=0.2)

Adjusting ternary plots Ternary plots have been around for a while in R; you could achieve the same thing with the vcd package authored by Michael Friendly. If you just need a quick and dirty ternary plot, that may suit you just fine. However, since ggtern is built on ggplot2, you can take advantage of all the tools available therein.

ggtern is authored by Nicholas Hamilton, more information can be found on his package website: www.ggtern.com.

The plot from the previous exercise is available twice. Can you adapt it in different ways to make different ternary density plots?

# ggtern and ggplot2 are loaded
# Original plot:
ggtern(africa, aes(x = Sand, y = Silt, z = Clay)) +
  geom_point(shape = 16, alpha = 0.2)
Warning message:
In read.dcf(file.path(p, "DESCRIPTION"), c("Package", "Version")) :
  cannot open compressed file '/Library/Frameworks/R.framework/Versions/3.4/Resources/library/GSIF/DESCRIPTION', probable reason 'No such file or directory'

# Plot 1
ggtern(africa, aes(x = Sand, y = Silt, z = Clay)) +
  geom_density_tern()

# Plot 2
ggtern(africa, aes(x = Sand, y = Silt, z = Clay)) +
  stat_density_tern(geom = "polygon", aes(fill = ..level.., alpha = ..level..)) +
  guides(fill = FALSE)

Build the network (1) Network data may be stored in a variety of ways.

For this example, you’ll use an undirected network of romantic relationships in the TV show Mad Men: geomnet::madmen.

# Load geomnet & examine structure of madmen
library(geomnet)
str(madmen)
List of 2
 $ edges   :'data.frame':   39 obs. of  2 variables:
  ..$ Name1: Factor w/ 9 levels "Betty Draper",..: 1 1 2 2 2 2 2 2 2 2 ...
  ..$ Name2: Factor w/ 39 levels "Abe Drexler",..: 15 31 2 4 5 6 8 9 11 21 ...
 $ vertices:'data.frame':   45 obs. of  2 variables:
  ..$ label : Factor w/ 45 levels "Abe Drexler",..: 5 9 16 23 26 32 33 38 39 17 ...
  ..$ Gender: Factor w/ 2 levels "female","male": 1 2 2 1 2 1 2 2 2 2 ...
# Merge edges and vertices
mmnet <- merge(madmen$edges, madmen$vertices,
               by.x = "Name1", by.y = "label",
               all = TRUE)
# Examine structure of mmnet
str(mmnet)
'data.frame':   75 obs. of  3 variables:
 $ Name1 : Factor w/ 45 levels "Betty Draper",..: 1 1 2 2 2 2 2 2 2 2 ...
 $ Name2 : Factor w/ 39 levels "Abe Drexler",..: 15 31 2 4 5 6 8 9 11 21 ...
 $ Gender: Factor w/ 2 levels "female","male": 1 1 2 2 2 2 2 2 2 2 ...

Build the network (2) Now that your data is in the correct format, you can build the actual network plot.

You’ll use the geom_net() function, a ggplot layer that’s in the geomnet package. The ggnetwork package is a popular alternative, but we will not discuss that here.

Can you finish the ggplot() command?

# geomnet is pre-loaded
# Merge edges and vertices
mmnet <- merge(madmen$edges, madmen$vertices,
               by.x = "Name1", by.y = "label",
               all = TRUE)
# Finish the ggplot command
ggplot(data = mmnet, aes(from_id = Name1, to_id = Name2)) +
  geom_net(aes(col=Gender), size=6, linewidth=1, labelon=TRUE, fontsize=3, labelcolour="black")

Adjusting the network Let’s clean up the network a bit. As you can see, since this is in the ggplot2 framework, you can manually adjust the scales like you have always done.

Here you’re going to use another trick to remove all theme elements and make a clean network plot.

# geomnet is pre-loaded
library(ggmap)
Google Maps API Terms of Service: http://developers.google.com/maps/terms.
Please cite ggmap if you use it: see citation('ggmap') for details.
# Merge edges and vertices
mmnet <- merge(madmen$edges, madmen$vertices,
               by.x = "Name1", by.y = "label",
               all = TRUE)
# Tweak the network plot
ggplot(data = mmnet, aes(from_id = Name1, to_id = Name2)) +
  geom_net(aes(col = Gender),
           size = 6,
           linewidth = 1,
           labelon = TRUE,
           fontsize = 3,
           labelcolour = "black",
           directed = TRUE) +
  scale_color_manual(values = c("#FF69B4", "#0099ff")) +
  xlim(c(-0.05, 1.05)) +
  ggmap::theme_nothing(legend = TRUE) +
  theme(legend.key = element_blank())
`panel.margin` is deprecated. Please use `panel.spacing` property instead

Autoplot on linear models R has several plotting methods for specific objects. For example using plot() on the results of an lm() call results in four plots that give you insight into how well the assigned model fits the data.

The ggfortify package is an all-purpose plot converter between base graphics and ggplot2 grid graphics.

You’ll explore exactly what we mean by graphics and grid in chapter 4. For now, just know that if you want to use the automatic output features in the context of ggplot2, they must first be converted to a ggplot object via ggfortify. This can be important at the superficial level, for consistency in appearance, but also at a deeper level, for later combining several plots in a single graphics device.

# Create linear model: res
res <- lm(Volume~Girth, data = trees)
# Plot res
plot(res)

# Import ggfortify and use autoplot()
library(ggfortify)
package 'ggfortify' was built under R version 3.4.4
autoplot(res, ncol=2)

ggfortify - time series Time series objects (class mts or ts) also have their own methods for plot(). ggfortify can also take advantage of this functionality.

In the workspace, you’ll find the variable Canada (it comes from the vars package): an mts class object with four series: prod is a measure of labour productivity, e is employment, U is the unemployment rate, and rw the real wage. They are each plotted as separate series by default.

# ggfortify and Canada are available
library(vars)
# Inspect structure of Canada
str(Canada)
 Time-Series [1:84, 1:4] from 1980 to 2001: 930 930 930 931 933 ...
 - attr(*, "dimnames")=List of 2
  ..$ : NULL
  ..$ : chr [1:4] "e" "prod" "rw" "U"
# Call plot() on Canada
plot(Canada)

# Call autoplot() on Canada
autoplot(Canada)

Distance matrices and Multi-Dimensional Scaling (MDS) As you can probably imagine, distance matrices (class dist) contain the measured distance between all pair-wise combinations of many points. For example, the eurodist dataset contains the distances between major European cities. dist objects lend themselves well to autoplot().

The cmdscale() function from the stats package performs Classical Multi-Dimensional Scaling and returns point coodinates as a matrix. Although autoplot() will work on this object, it will produce a heatmap, and not a scatter plot. However, if either eig = TRUE, add = TRUE or x.ret = TRUE is specified, cmdscale() will return a list instead of matrix. In these cases, the list method for autoplot() in the ggfortify package can deal with the output. Specifics on multi-dimensional scaling is beyond the scope of this course, however details on the method and these arguments can be found in the help pages ?cmdscale.

# ggfortify and eurodist are available
# Autoplot + ggplot2 tweaking
autoplot(eurodist) + 
  coord_fixed()
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.

# Autoplot of MDS
autoplot(cmdscale(eurodist, eig = TRUE), 
         label = TRUE, 
         label.size = 3, 
         size = 0)

Plotting K-means clustering ggfortify also supports stats::kmeans class objects. You must explicitly pass the original data to the autoplot function via the data argument, since kmeans objects don’t contain the original data. The result will be automatically colored according to cluster.

Here, you’ll use the iris dataset and just look at K-means clustering, although this works on many clustering methods, including cluster::clara(), cluster::fanny(), cluster::pam() and stats::prcomp(). Unfortunately a discussion of these clustering methods is beyond the scope of this course.

# Perform clustering
iris_k <- kmeans(iris[-5], 3)
# Autoplot: color according to cluster
autoplot(iris_k, data = iris, frame = TRUE)

# Autoplot: above, plus shape according to species
autoplot(iris_k, data = iris, frame = TRUE, shape='Species')

Working with maps from the maps package: USA The easiest way to obtain map polygons is through the maps package. Unfortunately there are only a few locations available, but if your region of interest is included they are extremely convenient.

The available maps of political boundaries are:

Global: world, world2 Country: france, italy, nz, usa USA: county, state The maps can be accessed via ggplot2::map_data(), which converts the map into a data frame containing the variables long and lat. To draw the map, you need to use geom_polygon() which will connect the points of latitude and longitude for you.

library(maps)
package 'maps' was built under R version 3.4.4
# maps, ggplot2, and ggmap are pre-loaded
# Use map_data() to create usa and inspect
usa <- map_data("usa")
str(usa)
'data.frame':   7243 obs. of  6 variables:
 $ long     : num  -101 -101 -101 -101 -101 ...
 $ lat      : num  29.7 29.7 29.7 29.6 29.6 ...
 $ group    : num  1 1 1 1 1 1 1 1 1 1 ...
 $ order    : int  1 2 3 4 5 6 7 8 9 10 ...
 $ region   : chr  "main" "main" "main" "main" ...
 $ subregion: chr  NA NA NA NA ...
# Build the map
ggplot(usa, aes(x = long, y = lat, group = group)) +
  geom_polygon() +
  coord_map() +
  theme_nothing()
`panel.margin` is deprecated. Please use `panel.spacing` property instead

The population pyramid Animations are particularly useful for temporal or geospatial data, and they are surprisingly easy to make! Here, you simply loop over the time variable in your dataset, composing a new plot for each subset in the data. These individual images are then cataloged in an animated GIF file.

To show this you’ll use a great animated population pyramid that was presented on the Revolutions blog. There are many more adjustments you could have made to the plot, but we’ll just make a barebones version here.

japan <- read.table("japanPOP.txt", header=TRUE)
head(japan)
# Inspect structure of japan
str(japan)
'data.frame':   8282 obs. of  4 variables:
 $ AGE : int  0 1 2 3 4 5 6 7 8 9 ...
 $ POP : int  -572954 -581748 -585239 -582223 -568788 -571899 -590530 -602349 -612527 -620373 ...
 $ time: int  2010 2010 2010 2010 2010 2010 2010 2010 2010 2010 ...
 $ SEX : Factor w/ 2 levels "Female","Male": 2 2 2 2 2 2 2 2 2 2 ...
library(animation)
# Finish the code inside saveGIF
saveGIF({
  # Loop through all time points
  for (i in unique(japan$time)) {
    # Subset japan: data
    data <- subset(japan, time == i)
    # Finish the ggplot command
    p <- ggplot(data, aes(x = AGE, y = POP, fill = SEX, width = 1)) +
      coord_flip() +
      geom_bar(data = data[data$SEX == "Female",], stat = "identity") +
      geom_bar(data = data[data$SEX == "Male",], stat = "identity") +
      ggtitle(i)
    print(p)
  }
}, movie.name = "pyramid.gif", interval = 0.1)
sh: convert: command not found
Error in cmd.fun(sprintf("%s --version", convert), intern = TRUE) : 
  error in running command
[1] FALSE
LS0tCnRpdGxlOiAiRGF0YSBWaXN1YWxpemF0aW9uIHdpdGggZ2dwbG90IDMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KClJlZnJlc2hlciAoMSkKQXMgYSByZWZyZXNoZXIgdG8gc3RhdGlzdGljYWwgcGxvdHMsIGxldCdzIGJ1aWxkIGEgc2NhdHRlciBwbG90IHdpdGggYW4gYWRkaXRpb25hbCBzdGF0aXN0aWMgbGF5ZXIuCgpBIGRhdGFzZXQgY2FsbGVkIG1vdmllc19zbWFsbCBpcyBjb2RlZCBpbiB5b3VyIHdvcmtzcGFjZS4gSXQgaXMgYSByYW5kb20gc2FtcGxlIG9mIDEwMDAgb2JzZXJ2YXRpb25zIGZyb20gdGhlIGxhcmdlciBtb3ZpZXMgZGF0YXNldCwgdGhhdCdzIGluc2lkZSB0aGUgZ2dwbG90Mm1vdmllcyBwYWNrYWdlLiBUaGUgZGF0YXNldCBjb250YWlucyBpbmZvcm1hdGlvbiBvbiBtb3ZpZXMgZnJvbSBJTURCLiBUaGUgdmFyaWFibGUgdm90ZXMgaXMgdGhlIG51bWJlciBvZiBJTURCIHVzZXJzIHdobyBoYXZlIHJhdGVkIGEgbW92aWUgYW5kIHRoZSByYXRpbmcgKGNvbnZlcnRlZCBpbnRvIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUpIGlzIHRoZSBhdmVyYWdlIHJhdGluZyBmb3IgdGhlIG1vdmllLgoKYGBge3J9CiMgQ3JlYXRlIG1vdmllc19zbWFsbApsaWJyYXJ5KGdncGxvdDJtb3ZpZXMpCmxpYnJhcnkoZ2dwbG90MikKc2V0LnNlZWQoMTIzKQptb3ZpZXNfc21hbGwgPC0gbW92aWVzW3NhbXBsZShucm93KG1vdmllcyksIDEwMDApLCBdCm1vdmllc19zbWFsbCRyYXRpbmcgPC0gZmFjdG9yKHJvdW5kKG1vdmllc19zbWFsbCRyYXRpbmcpKQoKIyBFeHBsb3JlIG1vdmllc19zbWFsbCB3aXRoIHN0cigpCnN0cihtb3ZpZXNfc21hbGwpCgojIEJ1aWxkIGEgc2NhdHRlciBwbG90IHdpdGggbWVhbiBhbmQgOTUlIENJCmdncGxvdChtb3ZpZXNfc21hbGwsIGFlcyh4ID0gcmF0aW5nLCB5ID0gdm90ZXMpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSAibWVhbl9jbF9ub3JtYWwiLAogICAgICAgICAgICAgICBnZW9tID0gImNyb3NzYmFyIiwKICAgICAgICAgICAgICAgd2lkdGggPSAwLjIsCiAgICAgICAgICAgICAgIGNvbCA9ICJyZWQiKSArCiAgc2NhbGVfeV9sb2cxMCgpCmBgYAoKUmVmcmVzaGVyICgyKQpUaGUgcGxvdCBpbiB0aGUgZ3JhcGhpY3MgZGV2aWNlIGlzIGEgdmFyaWF0aW9uIG9uIGFuIG9mdC1zZWVuIGdncGxvdDIgZXhhbXBsZSB1c2luZyB0aGUgZGlhbW9uZHMgZGF0YXNldCAoY29udGFpbmluZyBpbmZvcm1hdGlvbiBvbiBzZXZlcmFsIHZhcmlhYmxlcyBvZiBvdmVyIDUwLDAwMCBkaWFtb25kcykuCgpSZWNhbGwgdGhhdCB0aGVyZSBhcmUgYSB2YXJpZXR5IG9mIHNjYWxlXyBmdW5jdGlvbnMuIEhlcmUsIGRhdGEgYXJlIHRyYW5zZm9ybWVkIG9yIGZpbHRlcmVkIGZpcnN0LCBhZnRlciB3aGljaCB0aGUgcGxvdCBhbmQgYXNzb2NpYXRlZCBzdGF0aXN0aWNzIGFyZSBjb21wdXRlZC4gRm9yIGV4YW1wbGUsIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDEwMCwgMTAwMCkgd2lsbCByZW1vdmUgdmFsdWVzIG91dHNpZGUgdGhhdCByYW5nZS4KCkNvbnRyYXN0IHRoaXMgdG8gY29vcmRfY2FydGVzaWFuKCksIHdoaWNoIGNvbXB1dGVzIHRoZSBzdGF0aXN0aWNzIGJlZm9yZSBwbG90dGluZy4gVGhhdCBtZWFucyB0aGF0IHRoZSBwbG90IGFuZCBzdW1tYXJ5IHN0YXRpc3RpY3MgYXJlIHBlcmZvcm1lZCBvbiB0aGUgcmF3IGRhdGEuIFRoYXQncyB3aHkgd2Ugc2F5IHRoYXQgY29vcmRfY2FydGVzaWFuKGMoMTAwLCAxMDAwKSkgInpvb21zIGluIiBhIHBsb3QuIFRoaXMgd2FzIGRpc2N1c3NlZCBpbiB0aGUgY2hhcHRlciBvbiBjb29yZGluYXRlcyBpbiBjb3Vyc2UgMi4KCkhlcmUgd2UncmUgZ29pbmcgdG8gZXhwYW5kIG9uIHRoaXMgYW5kIGludHJvZHVjZSBzY2FsZV94X2xvZzEwKCkgYW5kIHNjYWxlX3lfbG9nMTAoKSB3aGljaCBwZXJmb3JtIGxvZzEwIHRyYW5zZm9ybWF0aW9ucywgYW5kIGNvb3JkX2VxdWFsKCksIHdoaWNoIHNldHMgYW4gYXNwZWN0IHJhdGlvIG9mIDEgKGNvb3JkX2ZpeGVkKCkgaXMgYWxzbyBhbiBvcHRpb24pLgoKWW91ciB0YXNrIGlzIHRvIHJlcHJvZHVjZSB0aGUgcGxvdCBpbiB0aGUgdmlld2VyLiBCZWZvcmUgeW91IGRvIHRoaXMsIGl0IG1pZ2h0IGJlIGEgZ29vZCBpZGVhIHRvIGV4cGxvcmUgZGlhbW9uZHMgaW4gdGhlIGNvbnNvbGUgaWYgeW91IGFyZSBub3QgZmFtaWxpYXIgd2l0aCBpdC4KCmBgYHtyfQojIFJlcHJvZHVjZSB0aGUgcGxvdApnZ3Bsb3QoZGlhbW9uZHMsIGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSwgY29sID0gY29sb3IpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSwgc2l6ZSA9IDAuNSwgc2hhcGUgPSAxNikgKwogIHNjYWxlX3hfbG9nMTAoZXhwcmVzc2lvbihsb2dbMTBdKENhcmF0KSksIGxpbWl0cyA9IGMoMC4xLDEwKSkgKwogIHNjYWxlX3lfbG9nMTAoZXhwcmVzc2lvbihsb2dbMTBdKFByaWNlKSksIGxpbWl0cyA9IGMoMTAwLDEwMDAwMCkpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJZbE9yUmQiKSArCiAgY29vcmRfZXF1YWwoKSArCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKUmVmcmVzaGVyICgzKQpUaGUgZ29hbCBwbG90IGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlIGlzIGNvZGVkIGluIHlvdXIgZWRpdG9yLiBIZXJlIHlvdSdsbCBleHBhbmQgb24gdGhpcyBwbG90IHdpdGggc3RhdF9zbW9vdGgoKSBtb2RlbCBpbnN0ZWFkIG9mIHNob3dpbmcgZXZlcnkgZGF0YSBwb2ludC4KCmBgYHtyfQojIEFkZCBzbW9vdGggbGF5ZXIgYW5kIGZhY2V0IHRoZSBwbG90CmdncGxvdChkaWFtb25kcywgYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlLCBjb2wgPSBjb2xvcikpICsKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iKSArCiAgc2NhbGVfeF9sb2cxMChleHByZXNzaW9uKGxvZ1sxMF0oQ2FyYXQpKSwgbGltaXRzID0gYygwLjEsMTApKSArCiAgc2NhbGVfeV9sb2cxMChleHByZXNzaW9uKGxvZ1sxMF0oUHJpY2UpKSwgbGltaXRzID0gYygxMDAsMTAwMDAwKSkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIllsT3JSZCIpICsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCgpUcmFuc2Zvcm1hdGlvbnMKSW4gdGhpcyBleGVyY2lzZSB5b3UnbGwgcmV0dXJuIHRvIHRoZSBmaXJzdCBwbG90dGluZyBleGVyY2lzZSBhbmQgc2VlIGhvdyBib3ggcGxvdHMgY29tcGFyZSB0byBkb3QgcGxvdHMgZm9yIHJlcHJlc2VudGluZyBoaWdoLWRlbnNpdHkgZGF0YS4KCkJveCBwbG90cyBhcmUgdmVyeSB1c2VmdWwsIGJ1dCB0aGV5IGRvbid0IHNvbHZlIGFsbCB5b3VyIHByb2JsZW1zIGFsbCB0aGUgdGltZSwgZm9yIGV4YW1wbGUsIHdoZW4geW91ciBkYXRhIGFyZSBoZWF2aWx5IHNrZXdlZCwgeW91IHdpbGwgc3RpbGwgbmVlZCB0byB0cmFuc2Zvcm0gaXQuIFlvdSdsbCBzZWUgdGhhdCBoZXJlLCB1c2luZyB0aGUgbW92aWVzX3NtYWxsIGRhdGFzZXQsIGEgc3Vic2V0IG9mIDEwLDAwMCBvYnNlcnZhdGlvbnMgb2YgZ2dwbG90Mm1vdmllczo6bW92aWVzLgoKYGBge3J9CiMgbW92aWVzX3NtYWxsIGlzIGF2YWlsYWJsZQoKIyBBZGQgYSBib3hwbG90IGdlb20KZCA8LSBnZ3Bsb3QobW92aWVzX3NtYWxsLCBhZXMoeCA9IHJhdGluZywgeSA9IHZvdGVzKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9ICJtZWFuX2NsX25vcm1hbCIsCiAgICAgICAgICAgICAgIGdlb20gPSAiY3Jvc3NiYXIiLAogICAgICAgICAgICAgICB3aWR0aCA9IDAuMiwKICAgICAgICAgICAgICAgY29sID0gInJlZCIpCgojIFVudHJhbnNmb3JtZWQgcGxvdApkCgojIFRyYW5zZm9ybSB0aGUgc2NhbGUKZCArIHNjYWxlX3lfbG9nMTAoKQoKIyBUcmFuc2Zvcm0gdGhlIGNvb3JkaW5hdGVzCmQgKyBjb29yZF90cmFucyh5ID0gImxvZzEwIikKYGBgCgpDdXQgaXQgdXAhCklmIHlvdSBvbmx5IGhhdmUgY29udGludW91cyB2YXJpYWJsZXMsIHlvdSBjYW4gY29udmVydCB0aGVtIGludG8gb3JkaW5hbCB2YXJpYWJsZXMgdXNpbmcgYW55IG9mIHRoZSBmb2xsb3dpbmcgZnVuY3Rpb25zOgoKY3V0X2ludGVydmFsKHgsIG4pIG1ha2VzIG4gZ3JvdXBzIGZyb20gdmVjdG9yIHggd2l0aCBlcXVhbCByYW5nZS4KY3V0X251bWJlcih4LCBuKSBtYWtlcyBuIGdyb3VwcyBmcm9tIHZlY3RvciB4IHdpdGggKGFwcHJveGltYXRlbHkpIGVxdWFsIG51bWJlcnMgb2Ygb2JzZXJ2YXRpb25zLgpjdXRfd2lkdGgoeCwgd2lkdGgpIG1ha2VzIGdyb3VwcyBvZiB3aWR0aCB3aWR0aCBmcm9tIHZlY3RvciB4LgpUaGlzIGlzIHVzZWZ1bCB3aGVuIHlvdSB3YW50IHRvIHN1bW1hcml6ZSBhIGNvbXBsZXggc2NhdHRlciBwbG90IGxpa2UgdGhlIG9uZSBzaG93biBpbiB0aGUgdmlld2VyLiBCeSBhcHBseWluZyB0aGVzZSBmdW5jdGlvbnMgdG8gdGhlIGNhcmF0IHZhcmlhYmxlIGFuZCBtYXBwaW5nIHRoYXQgb250byB0aGUgZ3JvdXAgYWVzdGhldGljLCB5b3UgY2FuIGNvbnZlcnQgdGhlIHNjYXR0ZXIgcGxvdCBpbiB0aGUgdmlld2VyIGludG8gYSBzZXJpZXMgb2YgYm94IHBsb3RzIG9uIHRoZSBmbHkuCgpgYGB7cn0KIyBQbG90IG9iamVjdCBwCnAgPC0gZ2dwbG90KGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKQoKIyBVc2UgY3V0X2ludGVydmFsCnAgKyBnZW9tX2JveHBsb3QoYWVzKGdyb3VwID0gY3V0X2ludGVydmFsKGNhcmF0LCBuPTEwKSkpCgojIFVzZSBjdXRfbnVtYmVyCnAgKyBnZW9tX2JveHBsb3QoYWVzKGdyb3VwID0gY3V0X251bWJlcihjYXJhdCwgbj0xMCkpKQoKIyBVc2UgY3V0X3dpZHRoCnAgKyBnZW9tX2JveHBsb3QoYWVzKGdyb3VwID0gY3V0X3dpZHRoKGNhcmF0LCB3aWR0aCA9IDAuMjUpKSkKYGBgCgpnZW9tX2RlbnNpdHkoKQpUbyBtYWtlIGEgc3RyYWlnaHRmb3J3YXJkIGRlbnNpdHkgcGxvdCwgYWRkIGEgZ2VvbV9kZW5zaXR5KCkgbGF5ZXIuCgpCZWZvcmUgcGxvdHRpbmcsIHlvdSB3aWxsIGNhbGN1bGF0ZSB0aGUgZW1wZXJpY2FsIGRlbnNpdHkgZnVuY3Rpb24sIHNpbWlsYXIgdG8gaG93IHlvdSBjYW4gdXNlIHRoZSBkZW5zaXR5KCkgZnVuY3Rpb24gaW4gdGhlIHN0YXRzIHBhY2thZ2UsIGF2YWlsYWJsZSBieSBkZWZhdWx0IHdoZW4geW91IHN0YXJ0IFIuIFRoZSBmb2xsb3dpbmcgZGVmYXVsdCBwYXJhbWV0ZXJzIGFyZSB1c2VkICh5b3UgY2FuIHNwZWNpZnkgdGhlc2UgYXJndW1lbnRzIGJvdGggaW4gZGVuc2l0eSgpIGFzIHdlbGwgYXMgZ2VvbV9kZW5zaXR5KCkpOgoKYncgPSAibnJkMCIsIHRlbGxpbmcgUiB3aGljaCBydWxlIHRvIHVzZSB0byBjaG9vc2UgYW4gYXBwcm9wcmlhdGUgYmFuZHdpZHRoLgprZXJuZWwgPSAiZ2F1c3NpYW4iLCB0ZWxsaW5nIFIgdG8gdXNlIHRoZSBHYXVzc2lhbiBrZXJuZWwuCldlJ3ZlIGFscmVhZHkgcHJlcGFyZWQgYSBkYXRhIGZyYW1lIHRlc3RfZGF0YSBmb3IgeW91LCBjb250YWluaW5nIHRocmVlIGNvbHVtbnM6IG5vcm0sIGJpbW9kYWwgYW5kIHVuaWZvcm0uIEVhY2ggY29sdW1uIHJlcHJlc2VudHMgMjAwIHNhbXBsZXMgZnJvbSBhIG5vcm1hbCwgYmltb2RhbCBhbmQgdW5pZm9ybSBkaXN0cmlidXRpb24uCgpgYGB7cn0Kcm4gPC0gcm5vcm0oMjAwLCAwLCAxKQoKYmltb2RhbERpc3RGdW5jIDwtIGZ1bmN0aW9uIChuLGNwY3QsIG11MSwgbXUyLCBzaWcxLCBzaWcyKSB7CiAgeTAgPC0gcmxub3JtKG4sbWVhbj1tdTEsIHNkID0gc2lnMSkKICB5MSA8LSBybG5vcm0obixtZWFuPW11Miwgc2QgPSBzaWcyKQoKICBmbGFnIDwtIHJiaW5vbShuLHNpemU9MSxwcm9iPWNwY3QpCiAgeSA8LSB5MCooMSAtIGZsYWcpICsgeTEqZmxhZyAKfQoKYm0gPC0gYmltb2RhbERpc3RGdW5jKG49MjAwLDAuNCwtMSwxLCAxLDEpCnVkIDwtIHJ1bmlmKDIwMCwgLTIsIDEpCnRlc3RfZGF0YSA8LSBkYXRhLmZyYW1lKCJub3JtIiA9IHJuLAogICAgICAgICAgICAgICAgICAgICAgICAiYmltb2RhbCIgPSBibSwKICAgICAgICAgICAgICAgICAgICAgICAgInVuaWZvcm0iID0gdWQpCmhlYWQodGVzdF9kYXRhKQoKYGBgCgpgYGB7cn0KIyB0ZXN0X2RhdGEgaXMgYXZhaWxhYmxlCgojIENhbGN1bGF0aW5nIGRlbnNpdHk6IGQKZCA8LSBkZW5zaXR5KHRlc3RfZGF0YSRub3JtKQoKIyBVc2Ugd2hpY2gubWF4KCkgdG8gY2FsY3VsYXRlIG1vZGUKbW9kZSA8LSBkJHhbd2hpY2gubWF4KGQkeSldCgojIEZpbmlzaCB0aGUgZ2dwbG90IGNhbGwKZ2dwbG90KHRlc3RfZGF0YSwgYWVzKHggPSBub3JtKSkgKwogIGdlb21fcnVnKCkgKwogIGdlb21fZGVuc2l0eSgpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtb2RlLCBjb2wgPSAicmVkIikKYGBgCgpDb21iaW5lIGRlbnNpdHkgcGxvdHMgYW5kIGhpc3RvZ3JhbQpTb21ldGltZXMgaXQgaXMgdXNlZnVsIHRvIGNvbXBhcmUgYSBoaXN0b2dyYW0gd2l0aCBhIGRlbnNpdHkgcGxvdC4gSG93ZXZlciwgdGhlIGhpc3RvZ3JhbSdzIHktc2NhbGUgbXVzdCBmaXJzdCBiZSBjb252ZXJ0ZWQgdG8gZnJlcXVlbmN5IGluc3RlYWQgb2YgYWJzb2x1dGUgY291bnQuIEFmdGVyIGRvaW5nIHNvLCB5b3UgY2FuIGFkZCBhbiBlbXBpcmljYWwgUERGIHVzaW5nIGdlb21fZGVuc2l0eSgpIG9yIGEgdGhlb3JldGljYWwgUERGIHVzaW5nIHN0YXRfZnVuY3Rpb24oKS4KCkNhbiB5b3UgZmluaXNoIHRoZSBwbG90IGJlbG93IGJ5IGZvbGxvd2luZyB0aGUgc3RlcHM/CgpgYGB7cn0KIyB0ZXN0X2RhdGEgaXMgYXZhaWxhYmxlCgojIEFyZ3VtZW50cyB5b3UnbGwgbmVlZCBsYXRlciBvbgpmdW5fYXJncyA8LSBsaXN0KG1lYW4gPSBtZWFuKHRlc3RfZGF0YSRub3JtKSwgc2QgPSBzZCh0ZXN0X2RhdGEkbm9ybSkpCgojIEZpbmlzaCB0aGUgZ2dwbG90CmdncGxvdCh0ZXN0X2RhdGEsIGFlcyh4ID0gbm9ybSkpICsKZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4pKSsKZ2VvbV9kZW5zaXR5KGNvbCA9ICJyZWQiKSArCnN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGFyZ3MgPSBmdW5fYXJncywgY29sPSJibHVlIikKYGBgCgpBZGp1c3RpbmcgZGVuc2l0eSBwbG90cwpUaGVyZSBhcmUgdGhyZWUgcGFyYW1ldGVycyB0aGF0IHlvdSBtYXkgYmUgdGVtcHRlZCB0byBhZGp1c3QgaW4gYSBkZW5zaXR5IHBsb3Q6CgpidyAtIHRoZSBzbW9vdGhpbmcgYmFuZHdpZHRoIHRvIGJlIHVzZWQsIHNlZSA/ZGVuc2l0eSBmb3IgZGV0YWlscwphZGp1c3QgLSBhZGp1c3RtZW50IG9mIHRoZSBiYW5kd2lkdGgsIHNlZSBkZW5zaXR5IGZvciBkZXRhaWxzCmtlcm5lbCAtIGtlcm5lbCB1c2VkIGZvciBkZW5zaXR5IGVzdGltYXRpb24sIGRlZmluZWQgYXMKImciID0gZ2F1c3NpYW4KInIiID0gcmVjdGFuZ3VsYXIKInQiID0gdHJpYW5ndWxhcgoiZSIgPSBlcGFuZWNobmlrb3YKImIiID0gYml3ZWlnaHQKImMiID0gY29zaW5lCiJvIiA9IG9wdGNvc2luZQpJbiB0aGlzIGV4ZXJjaXNlIHlvdSdsbCB1c2UgYSBkYXRhc2V0IGNvbnRhaW5pbmcgb25seSBmb3VyIHBvaW50cywgc21hbGxfZGF0YSwgc28gdGhhdCB5b3UgY2FuIHNlZSBob3cgdGhlc2UgdGhyZWUgYXJndW1lbnRzIGFmZmVjdCB0aGUgc2hhcGUgb2YgdGhlIGRlbnNpdHkgcGxvdC4KClRoZSB2ZWN0b3IgZ2V0X2J3IGNvbnRhaW5zIHRoZSBiYW5kd2lkdGggdGhhdCBpcyB1c2VkIGJ5IGRlZmF1bHQgaW4gZ2VvbV9kZW5zaXR5KCkuIHAgaXMgYSBiYXNpYyBwbG90dGluZyBvYmplY3QgdGhhdCB5b3UgY2FuIHN0YXJ0IGZyb20uCgpgYGB7cn0KIyBzbWFsbF9kYXRhIGlzIGF2YWlsYWJsZQpzbWFsbF9kYXRhIDwtIGRhdGEuZnJhbWUoIngiID0gYygtMy41LCAwLjAsMC41LCA2LjApKQoKIyBHZXQgdGhlIGJhbmR3aXRoCmdldF9idyA8LSBkZW5zaXR5KHNtYWxsX2RhdGEkeCkkYncKCiMgQmFzaWMgcGxvdHRpbmcgb2JqZWN0CnAgPC0gZ2dwbG90KHNtYWxsX2RhdGEsIGFlcyh4ID0geCkpICsKICBnZW9tX3J1ZygpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwwLjUpKQoKIyBDcmVhdGUgdGhyZWUgcGxvdHMKcCArIGdlb21fZGVuc2l0eSgpCnAgKyBnZW9tX2RlbnNpdHkoYWRqdXN0ID0gMC4yNSkKcCArIGdlb21fZGVuc2l0eShidyA9IDAuMjUgKiBnZXRfYncpCgojIENyZWF0ZSB0d28gcGxvdHMKcCArIGdlb21fZGVuc2l0eShrZXJuZWwgPSAiciIpCnAgKyBnZW9tX2RlbnNpdHkoa2VybmVsID0gImUiKQpgYGAKCkJveCBwbG90cyB3aXRoIHZhcnlpbmcgd2lkdGgKQSBkcmF3YmFjayBvZiBzaG93aW5nIGEgYm94IHBsb3QgcGVyIGdyb3VwLCBpcyB0aGF0IHlvdSBkb24ndCBoYXZlIGFueSBpbmRpY2F0aW9uIG9mIHRoZSBzYW1wbGUgc2l6ZSwgbiwgaW4gZWFjaCBncm91cCwgdGhhdCB3ZW50IGludG8gbWFraW5nIHRoZSBwbG90LiBPbmUgd2F5IG9mIGRlYWxpbmcgd2l0aCB0aGlzIGlzIHRvIHVzZSBhIHZhcmlhYmxlIHdpZHRoIGZvciB0aGUgYm94LCB3aGljaCByZWZsZWN0cyBkaWZmZXJlbmNlcyBpbiBuLgoKQ2FuIHlvdSBhZGQgc29tZSBnb29kLWxvb2tpbmcgYm94IHBsb3RzIHRvIHRoZSBiYXNpYyBwbG90IGNvZGVkIG9uIHRoZSByaWdodD8KCmBgYHtyfQojIEZpbmlzaCB0aGUgcGxvdApnZ3Bsb3QoZGlhbW9uZHMsIGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UsIGNvbCA9IGNvbG9yKSkgKwogIGdlb21fYm94cGxvdCh2YXJ3aWR0aCA9IFRSVUUpICsKICBmYWNldF9ncmlkKC4gfiBjb2xvcikKYGBgCgpNdWxpdHBsZSBkZW5zaXR5IHBsb3RzCkluIHRoaXMgZXhlcmNpc2UgeW91J2xsIGNvbWJpbmUgbXVsdGlwbGUgZGVuc2l0eSBwbG90cy4gSGVyZSwgeW91J2xsIGNvbWJpbmUganVzdCB0d28gZGlzdHJpYnV0aW9ucywgYSBub3JtYWwgYW5kIGEgYmltb2RhbC4KClRoZSBmaXJzdCB0aGluZyB0byByZW1lbWJlciBpcyB0aGF0IHlvdSBjYW4gY29uc2lkZXIgdmFsdWVzIGFzIHR3byBzZXBhcmF0ZSB2YXJpYWJsZXMsIGxpa2UgaW4gdGhlIHRlc3RfZGF0YSBkYXRhIGZyYW1lLCBvciBhcyBhIHNpbmdsZSBjb250aW51b3VzIHZhcmlhYmxlIHdpdGggdGhlaXIgSUQgYXMgYSBzZXBhcmF0ZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSwgbGlrZSBpbiB0aGUgdGVzdF9kYXRhMiBkYXRhIGZyYW1lLiB0ZXN0X2RhdGEyIGlzIG1vcmUgY29udmVuaWVudCBmb3IgY29tYmluaW5nIGFuZCBjb21wYXJpbmcgbXVsdGlwbGUgZGlzdHJpYnV0aW9ucy4KCmBgYHtyfQp0ZXN0X2RhdGEyIDwtIGRhdGEuZnJhbWUoImRpc3QiID0gYyhyZXAoIm5vcm0iLCAyMDApLCByZXAoImJpbW9kYWwiLCAyMDApKSwKICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgPSBjKHRlc3RfZGF0YSRub3JtLCB0ZXN0X2RhdGEkYmltb2RhbCkpCgojIHRlc3RfZGF0YSBhbmQgdGVzdF9kYXRhMiBhcmUgYXZhaWxhYmxlCnN0cih0ZXN0X2RhdGEpCnN0cih0ZXN0X2RhdGEyKQoKIyBQbG90IHdpdGggdGVzdF9kYXRhCmdncGxvdCh0ZXN0X2RhdGEsIGFlcyh4ID0gbm9ybSkpICsKICBnZW9tX3J1ZygpKwogIGdlb21fZGVuc2l0eSgpCgojIFBsb3QgdHdvIGRpc3RyaWJ1dGlvbnMgd2l0aCB0ZXN0X2RhdGEyCmdncGxvdCh0ZXN0X2RhdGEyLCBhZXMoeCA9IHZhbHVlLCBmaWxsID0gZGlzdCwgY29sID0gZGlzdCkpICsKICBnZW9tX3J1ZyhhbHBoYSA9IDAuNikgKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNikKCmBgYAoKTXVsdGlwbGUgZGVuc2l0eSBwbG90cyAoMikKV2hlbiB5b3UgbG9va2VkIGF0IG11bHRpcGxlIGJveCBwbG90cywgeW91IGNvbXBhcmVkIHRoZSB0b3RhbCBzbGVlcCB0aW1lIG9mIHZhcmlvdXMgbWFtbWFscywgc29ydGVkIGFjY29yZGluZyB0byB0aGVpciBlYXRpbmcgaGFiaXRzLiBPbmUgdGhpbmcgeW91IG5vdGVkIGlzIHRoYXQgZm9yIGluc2VjdGl2b3JlcywgYm94IHBsb3RzIGRpZG4ndCByZWFsbHkgbWFrZSBzZW5zZSwgc2luY2UgdGhlcmUgd2VyZSBvbmx5IDUgb2JzZXJ2YXRpb25zIHRvIGJlZ2luIHdpdGguIFlvdSBkZWNpZGVkIHRoYXQgeW91IGNvdWxkIG5vbmV0aGVsZXNzIHVzZSB0aGUgd2lkdGggb2YgYSBib3ggcGxvdCB0byBzaG93IHRoZSBkaWZmZXJlbmNlIGluIHNhbXBsZSBzaXplIGJldHdlZW4gdGhlIGdyb3Vwcy4gSGVyZSwgeW91J2xsIHNlZSBhIHNpbWlsYXIgdGhpbmcgd2l0aCBkZW5zaXR5IHBsb3RzLgoKQSBjbGVhbmVkIHVwIHZlcnNpb24gb2YgdGhlIG1hbW1hbGlhbiBkYXRhc2V0IGlzIGF2YWlsYWJsZSBhcyBtYW1tYWxzLgoKYGBge3J9CmhlYWQobXNsZWVwKQptYW1tYWxzIDwtIG1zbGVlcFssYygidm9yZSIsInNsZWVwX3RvdGFsIildCm1hbW1hbHMKYGBgCgpgYGB7cn0KIyBJbmRpdmlkdWFsIGRlbnNpdGllcwpnZ3Bsb3QobWFtbWFsc1ttYW1tYWxzJHZvcmUgPT0gIkluc2VjdGkiLCBdLCBhZXMoeCA9IHNsZWVwX3RvdGFsLCBmaWxsID0gdm9yZSkpICsKICBnZW9tX2RlbnNpdHkoY29sID0gTkEsIGFscGhhID0gMC4zNSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDI0KSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAwLjMpKQoKIyBXaXRoIGZhY2V0aW5nCmdncGxvdChtYW1tYWxzLCBhZXMoeCA9IHNsZWVwX3RvdGFsLCBmaWxsID0gdm9yZSkpICsKICBnZW9tX2RlbnNpdHkoY29sID0gTkEsIGFscGhhID0gMC4zNSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDI0KSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAwLjMpKSArCiAgZmFjZXRfd3JhcCggfiB2b3JlLCBucm93ID0gMikKCiMgTm90ZSB0aGF0IGJ5IGRlZmF1bHQsIHRoZSB4IHJhbmdlcyBmaWxsIHRoZSBzY2FsZQpnZ3Bsb3QobWFtbWFscywgYWVzKHggPSBzbGVlcF90b3RhbCwgZmlsbCA9IHZvcmUpKSArCiAgZ2VvbV9kZW5zaXR5KGNvbCA9IE5BLCBhbHBoYSA9IDAuMzUpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAyNCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwgMC4zKSkKCiMgVHJpbSBlYWNoIGRlbnNpdHkgcGxvdCBpbmRpdmlkdWFsbHkKZ2dwbG90KG1hbW1hbHMsIGFlcyh4ID0gc2xlZXBfdG90YWwsIGZpbGwgPSB2b3JlKSkgKwogIGdlb21fZGVuc2l0eShjb2wgPSBOQSwgYWxwaGEgPSAwLjM1LCB0cmltID0gVFJVRSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHM9YygwLDI0KSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAwLjMpKQpgYGAKCldlaWdodGVkIGRlbnNpdHkgcGxvdHMKV2hlbiBwbG90dGluZyBhIHNpbmdsZSB2YXJpYWJsZSwgdGhlIGRlbnNpdHkgcGxvdHMgKGFuZCB0aGVpciBiYW5kd2lkdGhzKSBhcmUgY2FsY3VsYXRlZCBzZXBhcmF0ZSBmb3IgZWFjaCB2YXJpYWJsZSAoc2VlIHRoZSBwbG90IGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlLCBwcm92aWRlZCkuCgpIb3dldmVyLCB3aGVuIHlvdSBjb21wYXJlIHNldmVyYWwgdmFyaWFibGVzIChzdWNoIGFzIGVhdGluZyBoYWJpdHMpIGl0J3MgdXNlZnVsIHRvIHNlZSB0aGUgZGVuc2l0eSBvZiBlYWNoIHN1YnNldCBpbiByZWxhdGlvbiB0byB0aGUgd2hvbGUgZGF0YSBzZXQuIFRoaXMgaG9sZHMgdHJ1ZSBmb3IgbXVsdGlwbGUgZGVuc2l0eSBwbG90cyBhcyB3ZWxsIGFzIGZvciB2aW9saW4gcGxvdHMuCgpGb3IgdGhpcywgd2UgbmVlZCB0byB3ZWlnaHQgdGhlIGRlbnNpdHkgcGxvdHMgc28gdGhhdCB0aGV5J3JlIHJlbGF0aXZlIHRvIGVhY2ggb3RoZXIuIEVhY2ggZGVuc2l0eSBwbG90IGlzIGFkanVzdGVkIGFjY29yZGluZyB0byB3aGF0IHByb3BvcnRpb24gb2YgdGhlIHRvdGFsIGRhdGEgc2V0IGVhY2ggc3ViLWdyb3VwIHJlcHJlc2VudHMuIFdlIGNhbGN1bGF0ZWQgdGhpcyB1c2luZyB0aGUgZHBseXIgY29tbWFuZHMgb24gbGluZXMgMTEtMTUuCgpUaGUgbWFtbWFscyBkYXRhIGZyYW1lIGlzIGF2YWlsYWJsZSBhcyBiZWZvcmUuIEFmdGVyIGV4ZWN1dGluZyB0aGUgY29tbW5hZHMsIGl0IHdpbGwgaGF2ZSB0aGUgdmFyaWFibGUgbiwgd2hpY2ggd2UnbGwgdXNlIGZvciB3ZWlnaHRpbmcuCgpgYGB7cn0KIyBVbndlaWdodGVkIGRlbnNpdHkgcGxvdCBmcm9tIGJlZm9yZQpnZ3Bsb3QobWFtbWFscywgYWVzKHggPSBzbGVlcF90b3RhbCwgZmlsbCA9IHZvcmUpKSArCiAgZ2VvbV9kZW5zaXR5KGNvbCA9IE5BLCBhbHBoYSA9IDAuMzUpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAyNCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwgMC4zKSkKCiMgVW53ZWlnaHRlZCB2aW9saW4gcGxvdApnZ3Bsb3QobWFtbWFscywgYWVzKHggPSB2b3JlLCB5ID0gc2xlZXBfdG90YWwsIGZpbGwgPSB2b3JlKSkgKwogIGdlb21fdmlvbGluKCkKCiMgQ2FsY3VsYXRlIHdlaWdodGluZyBtZWFzdXJlCmxpYnJhcnkoZHBseXIpCm1hbW1hbHMyIDwtIG1hbW1hbHMgJT4lCiAgZ3JvdXBfYnkodm9yZSkgJT4lCiAgbXV0YXRlKG4gPSBuKCkgLyBucm93KG1hbW1hbHMpKSAtPiBtYW1tYWxzCgojIFdlaWdodGVkIGRlbnNpdHkgcGxvdApnZ3Bsb3QobWFtbWFscywgYWVzKHggPSBzbGVlcF90b3RhbCwgZmlsbCA9IHZvcmUpKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyh3ZWlnaHQgPSBuKSwgY29sID0gTkEsIGFscGhhID0gMC4zNSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDI0KSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAwLjMpKQoKIyBXZWlnaHRlZCB2aW9saW4gcGxvdApnZ3Bsb3QobWFtbWFscywgYWVzKHggPSB2b3JlLCB5ID0gc2xlZXBfdG90YWwsIGZpbGwgPSB2b3JlKSkgKwogIGdlb21fdmlvbGluKGFlcyh3ZWlnaHQgPSBuKSwgY29sID0gTkEpCmBgYAoKMkQgZGVuc2l0eSBwbG90cyAoMSkKWW91IGNhbiBjb25zaWRlciB0d28gb3J0aG9nb25hbCBkZW5zaXR5IHBsb3RzIGluIHRoZSBmb3JtIG9mIGEgMkQgZGVuc2l0eSBwbG90LiBKdXN0IGxpa2Ugd2l0aCBhIDFEIGRlbnNpdHkgcGxvdCwgeW91IGNhbiBhZGp1c3QgdGhlIGJhbmR3aWR0aCBvZiBib3RoIGF4ZXMgaW5kZXBlbmRlbnRseS4KClRoZSBkYXRhIGlzIHN0b3JlZCBpbiB0aGUgZmFpdGhmdWwgZGF0YSBmcmFtZSwgYXZhaWxhYmxlIGluIHRoZSBkYXRhc2V0cyBwYWNrYWdlLiBUaGUgb2JqZWN0IHAgY29udGFpbnMgdGhlIGJhc2UgZGVmaW5pdGlvbnMgb2YgYSBwbG90LgoKYGBge3J9CiMgQmFzZSBsYXllcnMKcCA8LSBnZ3Bsb3QoZmFpdGhmdWwsIGFlcyh4ID0gd2FpdGluZywgeSA9IGVydXB0aW9ucykpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygxLCA1LjUpLCBleHBhbmQgPSBjKDAsIDApKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoNDAsIDEwMCksIGV4cGFuZCA9IGMoMCwgMCkpICsKICBjb29yZF9maXhlZCg2MCAvIDQuNSkKCiMgMSAtIFVzZSBnZW9tX2RlbnNpdHlfMmQoKQpwICsgZ2VvbV9kZW5zaXR5XzJkKCkKCiMgMiAtIFVzZSBzdGF0X2RlbnNpdHlfMmQoKSB3aXRoIGFyZ3VtZW50cwpwICsgc3RhdF9kZW5zaXR5XzJkKGFlcyhjb2wgPSAuLmxldmVsLi4pLCBoID0gYyg1LCAwLjUpKQpgYGAKCjJEIGRlbnNpdHkgcGxvdHMgKDIpCkNvbnRpbnVpbmcgd2l0aCB0aGUgZGVuc2l0eSBwbG90cyBmcm9tIHRoZSBsYXN0IGV4ZXJjaXNlLCBoZXJlIHlvdSdsbCBleHBsb3JlIHRoZSB2aXJpZGlzIHBhY2thZ2UuIFRoaXMgcGFja2FnZSBjb250YWlucyBtdWx0aS1odWUgY29sb3IgcGFsZXR0ZXMgc3VpdGFibGUgZm9yIGNvbnRpbnVvdXMgdmFyaWFibGVzLgoKVGhlIGFkdmFudGFnZSBvZiB0aGVzZSBzY2FsZXMgaXMgdGhhdCBpbnN0ZWFkIG9mIHByb3ZpZGluZyBhbiBldmVuIGNvbG9yIGdyYWRpZW50IGZvciBhIGNvbnRpbnVvdXMgc2NhbGUsIHRoZXkgaGlnaGxpZ2h0IHRoZSBoaWdoZXN0IHZhbHVlcyBieSB1c2luZyBhbiB1bmV2ZW4gY29sb3IgZ3JhZGllbnQgb24gcHVycG9zZS4gVGhlIGhpZ2ggdmFsdWVzIGFyZSBsaWdodGVyIGNvbG9ycyAoeWVsbG93IHZlcnN1cyBibHVlKSwgc28gdGhleSBzdGFuZCBvdXQgbW9yZS4KCkEgc2hhZGVkIDJEIGRlbnNpdHkgcGxvdCBzaG93aW5nIHRoZSBzYW1lIGRhdGEgYXMgdGhlIHByZXZpb3VzIGV4ZXJjaXNlIGhhcyBiZWVuIHByb3ZpZGVkIGZvciB5b3UuIFVwIHRvIHlvdSB0byB1cGdyYWRlIGl0IQoKYGBge3J9CiMgTG9hZCBpbiB0aGUgdmlyaWRpcyBwYWNrYWdlCmxpYnJhcnkodmlyaWRpcykKCiMgQWRkIHZpcmlkaXMgY29sb3Igc2NhbGUKZ2dwbG90KGZhaXRoZnVsLCBhZXMoeCA9IHdhaXRpbmcsIHkgPSBlcnVwdGlvbnMpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMSwgNS41KSwgZXhwYW5kID0gYygwLDApKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoNDAsIDEwMCksIGV4cGFuZCA9IGMoMCwwKSkgKwogIGNvb3JkX2ZpeGVkKDYwLzQuNSkgKwogIHN0YXRfZGVuc2l0eV8yZChnZW9tID0gInRpbGUiLCBhZXMoZmlsbCA9IC4uZGVuc2l0eS4uKSwgaD1jKDUsLjUpLCBjb250b3VyID0gRkFMU0UpKyBzY2FsZV9maWxsX3ZpcmlkaXMoKQpgYGAKClBhaXIgcGxvdHMgYW5kIGNvcnJlbGF0aW9uIG1hdHJpY2VzCk9uIHN0YXJ0dXAsIFIgZmVhdHVyZXMgdHdvIHVzZWZ1bCBxdWljay1hbmQtZGlydHkgcGFpcnMgcGxvdHMgZnVuY3Rpb25zLiBUaGV5IGJvdGggb25seSB0YWtlIGNvbnRpbnVvdXMgdmFyaWFibGVzLgoKWW91J2xsIGJlIHdvcmtpbmcgd2l0aCB0aGUgaXJpcyBkYXRhc2V0IGFuZCB3aXRoIG10Y2Fyc19mYWN0LCBhIHZlcnNpb24gb2YgbXRjYXJzIHdoZXJlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBoYXZlIGJlZW4gY29udmVydGVkIGludG8gYWN0dWFsIGZhY3RvciBjb2x1bW5zLgoKYGBge3J9CiMgcGFpcnMKcGFpcnMoaXJpc1sxOjRdKQoKIyBjaGFydC5Db3JyZWxhdGlvbgpsaWJyYXJ5KFBlcmZvcm1hbmNlQW5hbHl0aWNzKQpjaGFydC5Db3JyZWxhdGlvbihpcmlzWzE6NF0pCgojIGdncGFpcnMKbGlicmFyeShHR2FsbHkpCmdncGFpcnMoaXJpc1sxOjNdKQpgYGAKCkNyZWF0ZSBhIGNvcnJlbGF0aW9uIG1hdHJpeCBpbiBnZ3Bsb3QyCkluc3RlYWQgb2YgdXNpbmcgYW4gb2ZmLXRoZS1zaGVsZiBjb3JyZWxhdGlvbiBtYXRyaXggZnVuY3Rpb24sIHlvdSBjYW4gb2YgY291cnNlIGNyZWF0ZSB5b3VyIG93biBwbG90LiBKdXN0IGZvciBmdW4sIGluIHRoaXMgZXhlcmNpc2UsIHlvdSdsbCByZS1jcmVhdGUgdGhlIHNjYXR0ZXJwbG90IHlvdSBzZWUgb24gdGhlIHJpZ2h0LiBUaGUgc3RyZW5ndGggb2YgdGhlIGNvcnJlbGF0aW9uIGlzIGRlcGljdGVkIGJ5IHRoZSBzaXplIGFuZCBjb2xvciBvZiB0aGUgcG9pbnRzIGFuZCBsYWJlbHMuCgpGb3Igc3RhcnRlcnMsIGEgY29ycmVsYXRpb24gbWF0cml4IGNhbiBiZSBjYWxjdWxhdGVkIHVzaW5nLCBmb3IgZXhhbXBsZSwgY29yKGRhdGFmcmFtZSkgKGlmIGFsbCB2YXJpYWJsZXMgYXJlIG51bWVyaWNhbCkuIEJlZm9yZSB5b3UgY2FuIHVzZSB5b3VyIGRhdGEgZnJhbWUgdG8gY3JlYXRlIHlvdXIgb3duIGNvcnJlbGF0aW9uIG1hdHJpeCBwbG90LCB5b3UnbGwgbmVlZCB0byBnZXQgaXQgaW4gdGhlIHJpZ2h0IGZvcm1hdC4KCkluIHRoZSBlZGl0b3IsIHlvdSBjYW4gc2VlIHRoZSBkZWZpbml0aW9uIG9mIGNvcl9saXN0KCksIGEgZnVuY3Rpb24gdGhhdCByZS1mb3JtYXRzIHRoZSBkYXRhIGZyYW1lIHguIEhlcmUsIEwgaXMgdXNlZCB0byBhZGQgdGhlIHBvaW50cyB0byB0aGUgbG93ZXIgdHJpYW5nbGUgb2YgdGhlIG1hdHJpeCwgYW5kIE0gaXMgdXNlZCB0byBhZGQgdGhlIG51bWVyaWNhbCB2YWx1ZXMgYXMgdGV4dCB0byB0aGUgdXBwZXIgdHJpYW5nbGUgb2YgdGhlIG1hdHJpeC4gV2l0aCByZXNoYXBlMjo6bWVsdCgpLCB0aGUgY29ycmVsYXRpb24gbWF0cmljZXMgTCBhbmQgTSBhcmUgZWFjaCBjb252ZXJ0ZWQgaW50byBhIHRocmVlLWNvbHVtbiBkYXRhIGZyYW1lOiB0aGUgeCBhbmQgeSBheGVzIG9mIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXggbWFrZSB1cCB0aGUgZmlyc3QgdHdvIGNvbHVtbnMgYW5kIHRoZSBjb3JyZXNwb25kaW5nIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IG1ha2VzIHVwIHRoZSB0aGlyZCBjb2x1bW4uIFRoZXNlIGJlY29tZSB0aGUgbmV3IHZhcmlhYmxlcyAicG9pbnRzIiBhbmQgImxhYmVscyIsIHdoaWNoIGNhbiBiZSBtYXBwZWQgb250byB0aGUgc2l6ZSBhZXN0aGV0aWMgZm9yIHRoZSBwb2ludHMgaW4gdGhlIGxvd2VyIHRyaWFuZ2xlIGFuZCBvbnRvIHRoZSBsYWJlbCBhZXN0aGV0aWMgZm9yIHRoZSB0ZXh0IGluIHRoZSB1cHBlciB0cmlhbmdsZSwgcmVzcGVjdGl2ZWx5LiBUaGVpciB2YWx1ZXMgd2lsbCBiZSB0aGUgc2FtZSwgYnV0IHRoZWlyIHBvc2l0aW9ucyBvbiB0aGUgcGxvdCB3aWxsIGJlIHN5bW1ldHJpY2FsIGFib3V0IHRoZSBkaWFnb25hbCEgTWVyZ2luZyBMIGFuZCBNLCB5b3UgaGF2ZSBldmVyeXRoaW5nIHlvdSBuZWVkLgoKSWYgeW91J3JlIG5vdCBmYW1pbGlhciB3aXRoIHJlc2hhcGUyIC0gZG9uJ3Qgd29ycnksIHRoZSBvbmx5IHJlYXNvbiB3ZSB1c2UgdGhhdCBpbnN0ZWFkIG9mIHRpZHlyIGlzIHRoYXQgcmVzaGFwZTI6Om1lbHQoKSBjYW4gaGFuZGxlIGEgbWF0cml4LCB3aGVyZWFzIHRpZHlyOjpnYXRoZXIoKSByZXF1aXJlcyBhIGRhdGEgZnJhbWUuIEF0IHRoaXMgcG9pbnQgeW91IGp1c3QgbmVlZCB0byB1bmRlcnN0YW5kIGhvdyB0byB1c2UgdGhlIG91dHB1dCBmcm9tIGNvcl9saXN0KCkuCgpZb3UnbGwgZmlyc3QgdXNlIGRwbHlyIHRvIGV4ZWN1dGUgdGhpcyBmdW5jdGlvbiBvbiB0aGUgY29udGludW91cyB2YXJpYWJsZXMgaW4gdGhlIGlyaXMgZGF0YSBmcmFtZSAodGhlIGZpcnN0IGZvdXIgY29sdW1ucyksIGJ1dCBzZXBhcmF0ZWx5IGZvciBlYWNoIHNwZWNpZXMuIFBsZWFzZSByZWZlciB0byB0aGUgY291cnNlIG9uIGRwbHlyIGlmIHlvdSBhcmUgbm90IGZhbWlsaWFyIHdpdGggdGhlc2UgZnVuY3Rpb25zLgoKTmV4dCwgeW91J2xsIGFjdHVhbGx5IHBsb3QgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIHdpdGggZ2dwbG90MiBmdW5jdGlvbnMuCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJlc2hhcGUyKQoKY29yX2xpc3QgPC0gZnVuY3Rpb24oeCkgewogIEwgPC0gTSA8LSBjb3IoeCkKICAKICBNW2xvd2VyLnRyaShNLCBkaWFnID0gVFJVRSldIDwtIE5BCiAgTSA8LSBtZWx0KE0pCiAgbmFtZXMoTSlbM10gPC0gInBvaW50cyIKICAKICBMW3VwcGVyLnRyaShMLCBkaWFnID0gVFJVRSldIDwtIE5BCiAgTCA8LSBtZWx0KEwpCiAgbmFtZXMoTClbM10gPC0gImxhYmVscyIKICAKICBtZXJnZShNLCBMKQp9CgojIENhbGN1bGF0ZSB4eCB3aXRoIGNvcl9saXN0CmxpYnJhcnkoZHBseXIpCnh4IDwtIGlyaXMgJT4lCiAgZ3JvdXBfYnkoU3BlY2llcykgJT4lCiAgZG8oY29yX2xpc3QoLlsxOjRdKSkgCgojIEZpbmlzaCB0aGUgcGxvdApnZ3Bsb3QoeHgsIGFlcyh4ID0gVmFyMSwgeSA9IFZhcjIpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sID0gcG9pbnRzLCBzaXplID0gYWJzKHBvaW50cykpLCBzaGFwZSA9IDE2KSArCiAgZ2VvbV90ZXh0KGFlcyhjb2wgPSBsYWJlbHMsICBzaXplID0gYWJzKGxhYmVscyksIGxhYmVsID0gcm91bmQobGFiZWxzLCAyKSkpICsKICBzY2FsZV9zaXplKHJhbmdlID0gYygwLCA2KSkgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50MigiciIsIGxpbWl0cyA9IGMoLTEsIDEpKSArCiAgc2NhbGVfeV9kaXNjcmV0ZSgiIiwgbGltaXRzID0gcmV2KGxldmVscyh4eCRWYXIxKSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKCIiKSArCiAgZ3VpZGVzKHNpemUgPSBGQUxTRSkgKwogIGdlb21fYWJsaW5lKHNsb3BlID0gLTEsIGludGVyY2VwdCA9IG5sZXZlbHMoeHgkVmFyMSkgKyAxKSArCiAgY29vcmRfZml4ZWQoKSArCiAgZmFjZXRfZ3JpZCguIH4gU3BlY2llcykgKwogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQpgYGAKClByb3BvcnRpb25hbC9zdGFja2VkIGJhciBwbG90cwpCZWZvcmUgeW91IGhlYWQgb3ZlciB0byB0ZXJuYXJ5IHBsb3RzLCBsZXQncyB0cnkgdG8gbWFrZSBhIGNsYXNzaWNhbCBwcm9wb3J0aW9uYWwvc3RhY2tlZCBiYXIgcGxvdCBvZiBhIHN1YnNldCBvZiB0aGUgZGF0YS4gV2UnbGwgdXNlIGEgc3RhY2tlZCBiYXIgcGxvdCBhbmQgdGhlIGNvb3JkX2ZsaXAoKSBmdW5jdGlvbiB0byBmbGlwcyB0aGUgeCBhbmQgeSBheGVzLgoKVGhlIGRhdGEgZnJhbWUgZm9yIHRoZSBBZnJpY2FuIFNvaWwgUHJvZmlsZXMgRGF0YWJhc2UgaXMgYXZhaWxhYmxlIGluIHlvdXIgd29ya3NwYWNlIGFzIGFmcmljYSBhbmQgY2FuIGJlIGZvdW5kIGluIHRoZSBHU0lGIHBhY2thZ2UuIEl0IGNvbnRhaW5zIHRocmVlIGNvbHVtbnM6IFNhbmQsIFNpbHQgYW5kIENsYXkuIEEgc21hbGxlciB2ZXJzaW9uLCBjb250YWluaW5nIG9ubHkgNTAgb2JzZXJ2YXRpb25zIGlzIHN0b3JlZCBpbiBhZnJpY2Ffc2FtcGxlLgoKSW4gdGhlIGZpcnN0IGNvdXJzZSB3ZSBtZW50aW9uZWQgdGhhdCBpbiB0aGUgZGF0YSBsYXllciwgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YSBzaG91bGQgcmVmbGVjdCBob3cgeW91IHdpc2ggdG8gcGxvdCBpdC4gRm9yIGEgdGVybmFyeSBwbG90LCB5b3UgbmVlZCB0byBoYXZlIHRocmVlIHNlcGFyYXRlIHZhcmlhYmxlcywgZm9yIGV4YW1wbGUsIFNhbmQsIFNpbHQgYW5kIENsYXkgaW4gYWZyaWNhLiBIb3dldmVyLCBmb3IgYSBwcm9wb3J0aW9uYWwvc3RhY2tlZCBiYXIgcGxvdCwgeW91IGp1c3QgbmVlZCB0d28uIFRoZSB0eXBlIHNob3VsZCBiZSBkZWZpbmVkIGFzIHRocmVlIGxldmVscyB3aXRoaW4gYSBzaW5nbGUgZmFjdG9yIHZhcmlhYmxlLiBUaGF0IGlzLCB5b3Ugd2FudCB0aWR5IGRhdGEuCgpJdCdzIGFsc28gdXNlZnVsIHRvIG1haW50YWluIHRoZSBzaXRlIElEcyBhcyBhIHZhcmlhYmxlIHdpdGhpbiB0aGUgZGF0YSBmcmFtZSwgY3VycmVudGx5LCB0aGV5IGFyZSBzdG9yZWQgYXQgcm93IG5hbWVzLCB3aGljaCBpcyBwb29yIHN0eWxlIGFuZCBub3QgdXNlZnVsLgoKYGBge3J9CgojIEV4cGxvcmUgYWZyaWNhCnN0cihhZnJpY2EpCmFmcmljYV9zYW1wbGUgPC0gYWZyaWNhW3NhbXBsZShucm93KGFmcmljYSksIDUwKSwgXQpzdHIoYWZyaWNhX3NhbXBsZSkKCiMgQWRkIGFuIElEIGNvbHVtbiBmcm9tIHRoZSByb3cubmFtZXMKYWZyaWNhX3NhbXBsZSRJRCA8LSByb3cubmFtZXMoYWZyaWNhX3NhbXBsZSkKCiMgR2F0aGVyIGFmcmljYV9zYW1wbGUKbGlicmFyeSh0aWR5cikKYWZyaWNhX3NhbXBsZV90aWR5IDwtIGdhdGhlcihhZnJpY2Ffc2FtcGxlLCBrZXksIHZhbHVlLCAtSUQpCmhlYWQoYWZyaWNhX3NhbXBsZV90aWR5KQoKIyBGaW5pc2ggdGhlIGdncGxvdCBjb21tYW5kCmdncGxvdChhZnJpY2Ffc2FtcGxlX3RpZHksIGFlcyh4ID0gZmFjdG9yKElEKSwgeSA9IHZhbHVlLCBmaWxsID0ga2V5KSkgKwogIGdlb21fY29sKCkgKwogIGNvb3JkX2ZsaXAoKQpgYGAKClByb2R1Y2luZyB0ZXJuYXJ5IHBsb3RzCk9rLCBsZXQncyBtb3ZlIG9udG8gdGVybmFyeSBwbG90cy4gRm9yIHRoaXMgeW91J2xsIHVzZSB0aGUgZ2d0ZXJuIHBhY2thZ2UsIHdoaWNoIHByb3ZpZGVzIHRoZSBnZ3Rlcm4oKSBmdW5jdGlvbi4KCkluIGNvbnRyYXN0IHRvIHdoYXQgeW91IGp1c3Qgc2F3IGluIGFmcmljYV9zbWFsbF90aWR5LCB0aGUgdGhyZWUgc29pbCBwcm9wZXJ0aWVzLCBTYW5kLCBTaWx0IGFuZCBDbGF5LCBhcmUgbm90IGdvaW5nIHRvIGJlIGxvY2F0ZWQgaW4gYSBzaW5nbGUgdmFyaWFibGUuIFRoZSBkaXN0aW5jdGlvbiBiZXR3ZWVuIHdpZGUgYW5kIHRpZHkgZm9ybWF0IGRhdGEgd2FzIGRpc2N1c3NlZCBpbiB0aGUgZmlyc3QgY291cnNlIGFuZCBoZXJlIHlvdSdsbCBzZWUgaXQgaW4gYWN0aW9uLiBTb21ldGltZXMgeW91IG5lZWQgdG8gcmVhcnJhbmdlIHlvdXIgZGF0YSBmb3IgdGhlIGRlc2lyZWQgcGxvdCB0eXBlLgoKSGVyZSwgeW91J2xsIHVzZSB0aGUgY29tcGxldGUgZGF0YXNldCwgYWZyaWNhLCBjb250YWluaW5nIHRocmVlIHNlcGFyYXRlIHZhcmlhYmxlcyBmb3IgdGhlIG1lYXN1cmVzIG9mIGludGVyZXN0OiB0aGF0IGZvcm1hdCBpcyBwZXJmZWN0IGZvciBhIHRlcm5hcnkgcGxvdC4KCmBgYHtyfQojIExvYWQgZ2d0ZXJuCmxpYnJhcnkoZ2d0ZXJuKQoKIyBCdWlsZCB0ZXJuYXJ5IHBsb3QKZ2d0ZXJuKGFmcmljYSwgYWVzKHggPSBTYW5kLCB5ID0gU2lsdCwgeiA9IENsYXkpKSArCiAgZ2VvbV9wb2ludChzaGFwZT0xNiwgYWxwaGE9MC4yKQpgYGAKCkFkanVzdGluZyB0ZXJuYXJ5IHBsb3RzClRlcm5hcnkgcGxvdHMgaGF2ZSBiZWVuIGFyb3VuZCBmb3IgYSB3aGlsZSBpbiBSOyB5b3UgY291bGQgYWNoaWV2ZSB0aGUgc2FtZSB0aGluZyB3aXRoIHRoZSB2Y2QgcGFja2FnZSBhdXRob3JlZCBieSBNaWNoYWVsIEZyaWVuZGx5LiBJZiB5b3UganVzdCBuZWVkIGEgcXVpY2sgYW5kIGRpcnR5IHRlcm5hcnkgcGxvdCwgdGhhdCBtYXkgc3VpdCB5b3UganVzdCBmaW5lLiBIb3dldmVyLCBzaW5jZSBnZ3Rlcm4gaXMgYnVpbHQgb24gZ2dwbG90MiwgeW91IGNhbiB0YWtlIGFkdmFudGFnZSBvZiBhbGwgdGhlIHRvb2xzIGF2YWlsYWJsZSB0aGVyZWluLgoKZ2d0ZXJuIGlzIGF1dGhvcmVkIGJ5IE5pY2hvbGFzIEhhbWlsdG9uLCBtb3JlIGluZm9ybWF0aW9uIGNhbiBiZSBmb3VuZCBvbiBoaXMgcGFja2FnZSB3ZWJzaXRlOiB3d3cuZ2d0ZXJuLmNvbS4KClRoZSBwbG90IGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlIGlzIGF2YWlsYWJsZSB0d2ljZS4gQ2FuIHlvdSBhZGFwdCBpdCBpbiBkaWZmZXJlbnQgd2F5cyB0byBtYWtlIGRpZmZlcmVudCB0ZXJuYXJ5IGRlbnNpdHkgcGxvdHM/CgpgYGB7cn0KIyBnZ3Rlcm4gYW5kIGdncGxvdDIgYXJlIGxvYWRlZAojIE9yaWdpbmFsIHBsb3Q6CmdndGVybihhZnJpY2EsIGFlcyh4ID0gU2FuZCwgeSA9IFNpbHQsIHogPSBDbGF5KSkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxNiwgYWxwaGEgPSAwLjIpCgojIFBsb3QgMQpnZ3Rlcm4oYWZyaWNhLCBhZXMoeCA9IFNhbmQsIHkgPSBTaWx0LCB6ID0gQ2xheSkpICsKICBnZW9tX2RlbnNpdHlfdGVybigpCgojIFBsb3QgMgpnZ3Rlcm4oYWZyaWNhLCBhZXMoeCA9IFNhbmQsIHkgPSBTaWx0LCB6ID0gQ2xheSkpICsKICBzdGF0X2RlbnNpdHlfdGVybihnZW9tID0gInBvbHlnb24iLCBhZXMoZmlsbCA9IC4ubGV2ZWwuLiwgYWxwaGEgPSAuLmxldmVsLi4pKSArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkKYGBgCgpCdWlsZCB0aGUgbmV0d29yayAoMSkKTmV0d29yayBkYXRhIG1heSBiZSBzdG9yZWQgaW4gYSB2YXJpZXR5IG9mIHdheXMuCgpGb3IgdGhpcyBleGFtcGxlLCB5b3UnbGwgdXNlIGFuIHVuZGlyZWN0ZWQgbmV0d29yayBvZiByb21hbnRpYyByZWxhdGlvbnNoaXBzIGluIHRoZSBUViBzaG93IE1hZCBNZW46IGdlb21uZXQ6Om1hZG1lbi4KCmBgYHtyfQojIExvYWQgZ2VvbW5ldCAmIGV4YW1pbmUgc3RydWN0dXJlIG9mIG1hZG1lbgpsaWJyYXJ5KGdlb21uZXQpCnN0cihtYWRtZW4pCgojIE1lcmdlIGVkZ2VzIGFuZCB2ZXJ0aWNlcwptbW5ldCA8LSBtZXJnZShtYWRtZW4kZWRnZXMsIG1hZG1lbiR2ZXJ0aWNlcywKICAgICAgICAgICAgICAgYnkueCA9ICJOYW1lMSIsIGJ5LnkgPSAibGFiZWwiLAogICAgICAgICAgICAgICBhbGwgPSBUUlVFKQoKIyBFeGFtaW5lIHN0cnVjdHVyZSBvZiBtbW5ldApzdHIobW1uZXQpCmBgYAoKQnVpbGQgdGhlIG5ldHdvcmsgKDIpCk5vdyB0aGF0IHlvdXIgZGF0YSBpcyBpbiB0aGUgY29ycmVjdCBmb3JtYXQsIHlvdSBjYW4gYnVpbGQgdGhlIGFjdHVhbCBuZXR3b3JrIHBsb3QuCgpZb3UnbGwgdXNlIHRoZSBnZW9tX25ldCgpIGZ1bmN0aW9uLCBhIGdncGxvdCBsYXllciB0aGF0J3MgaW4gdGhlIGdlb21uZXQgcGFja2FnZS4gVGhlIGdnbmV0d29yayBwYWNrYWdlIGlzIGEgcG9wdWxhciBhbHRlcm5hdGl2ZSwgYnV0IHdlIHdpbGwgbm90IGRpc2N1c3MgdGhhdCBoZXJlLgoKQ2FuIHlvdSBmaW5pc2ggdGhlIGdncGxvdCgpIGNvbW1hbmQ/CgpgYGB7cn0KIyBnZW9tbmV0IGlzIHByZS1sb2FkZWQKCiMgTWVyZ2UgZWRnZXMgYW5kIHZlcnRpY2VzCm1tbmV0IDwtIG1lcmdlKG1hZG1lbiRlZGdlcywgbWFkbWVuJHZlcnRpY2VzLAogICAgICAgICAgICAgICBieS54ID0gIk5hbWUxIiwgYnkueSA9ICJsYWJlbCIsCiAgICAgICAgICAgICAgIGFsbCA9IFRSVUUpCgojIEZpbmlzaCB0aGUgZ2dwbG90IGNvbW1hbmQKZ2dwbG90KGRhdGEgPSBtbW5ldCwgYWVzKGZyb21faWQgPSBOYW1lMSwgdG9faWQgPSBOYW1lMikpICsKICBnZW9tX25ldChhZXMoY29sPUdlbmRlciksIHNpemU9NiwgbGluZXdpZHRoPTEsIGxhYmVsb249VFJVRSwgZm9udHNpemU9MywgbGFiZWxjb2xvdXI9ImJsYWNrIikKYGBgCgpBZGp1c3RpbmcgdGhlIG5ldHdvcmsKTGV0J3MgY2xlYW4gdXAgdGhlIG5ldHdvcmsgYSBiaXQuIEFzIHlvdSBjYW4gc2VlLCBzaW5jZSB0aGlzIGlzIGluIHRoZSBnZ3Bsb3QyIGZyYW1ld29yaywgeW91IGNhbiBtYW51YWxseSBhZGp1c3QgdGhlIHNjYWxlcyBsaWtlIHlvdSBoYXZlIGFsd2F5cyBkb25lLgoKSGVyZSB5b3UncmUgZ29pbmcgdG8gdXNlIGFub3RoZXIgdHJpY2sgdG8gcmVtb3ZlIGFsbCB0aGVtZSBlbGVtZW50cyBhbmQgbWFrZSBhIGNsZWFuIG5ldHdvcmsgcGxvdC4KCmBgYHtyfQojIGdlb21uZXQgaXMgcHJlLWxvYWRlZApsaWJyYXJ5KGdnbWFwKQojIE1lcmdlIGVkZ2VzIGFuZCB2ZXJ0aWNlcwptbW5ldCA8LSBtZXJnZShtYWRtZW4kZWRnZXMsIG1hZG1lbiR2ZXJ0aWNlcywKICAgICAgICAgICAgICAgYnkueCA9ICJOYW1lMSIsIGJ5LnkgPSAibGFiZWwiLAogICAgICAgICAgICAgICBhbGwgPSBUUlVFKQoKIyBUd2VhayB0aGUgbmV0d29yayBwbG90CmdncGxvdChkYXRhID0gbW1uZXQsIGFlcyhmcm9tX2lkID0gTmFtZTEsIHRvX2lkID0gTmFtZTIpKSArCiAgZ2VvbV9uZXQoYWVzKGNvbCA9IEdlbmRlciksCiAgICAgICAgICAgc2l6ZSA9IDYsCiAgICAgICAgICAgbGluZXdpZHRoID0gMSwKICAgICAgICAgICBsYWJlbG9uID0gVFJVRSwKICAgICAgICAgICBmb250c2l6ZSA9IDMsCiAgICAgICAgICAgbGFiZWxjb2xvdXIgPSAiYmxhY2siLAogICAgICAgICAgIGRpcmVjdGVkID0gVFJVRSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjRkY2OUI0IiwgIiMwMDk5ZmYiKSkgKwogIHhsaW0oYygtMC4wNSwgMS4wNSkpICsKICBnZ21hcDo6dGhlbWVfbm90aGluZyhsZWdlbmQgPSBUUlVFKSArCiAgdGhlbWUobGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpBdXRvcGxvdCBvbiBsaW5lYXIgbW9kZWxzClIgaGFzIHNldmVyYWwgcGxvdHRpbmcgbWV0aG9kcyBmb3Igc3BlY2lmaWMgb2JqZWN0cy4gRm9yIGV4YW1wbGUgdXNpbmcgcGxvdCgpIG9uIHRoZSByZXN1bHRzIG9mIGFuIGxtKCkgY2FsbCByZXN1bHRzIGluIGZvdXIgcGxvdHMgdGhhdCBnaXZlIHlvdSBpbnNpZ2h0IGludG8gaG93IHdlbGwgdGhlIGFzc2lnbmVkIG1vZGVsIGZpdHMgdGhlIGRhdGEuCgpUaGUgZ2dmb3J0aWZ5IHBhY2thZ2UgaXMgYW4gYWxsLXB1cnBvc2UgcGxvdCBjb252ZXJ0ZXIgYmV0d2VlbiBiYXNlIGdyYXBoaWNzIGFuZCBnZ3Bsb3QyIGdyaWQgZ3JhcGhpY3MuCgpZb3UnbGwgZXhwbG9yZSBleGFjdGx5IHdoYXQgd2UgbWVhbiBieSBncmFwaGljcyBhbmQgZ3JpZCBpbiBjaGFwdGVyIDQuIEZvciBub3csIGp1c3Qga25vdyB0aGF0IGlmIHlvdSB3YW50IHRvIHVzZSB0aGUgYXV0b21hdGljIG91dHB1dCBmZWF0dXJlcyBpbiB0aGUgY29udGV4dCBvZiBnZ3Bsb3QyLCB0aGV5IG11c3QgZmlyc3QgYmUgY29udmVydGVkIHRvIGEgZ2dwbG90IG9iamVjdCB2aWEgZ2dmb3J0aWZ5LiBUaGlzIGNhbiBiZSBpbXBvcnRhbnQgYXQgdGhlIHN1cGVyZmljaWFsIGxldmVsLCBmb3IgY29uc2lzdGVuY3kgaW4gYXBwZWFyYW5jZSwgYnV0IGFsc28gYXQgYSBkZWVwZXIgbGV2ZWwsIGZvciBsYXRlciBjb21iaW5pbmcgc2V2ZXJhbCBwbG90cyBpbiBhIHNpbmdsZSBncmFwaGljcyBkZXZpY2UuCgpgYGB7cn0KIyBDcmVhdGUgbGluZWFyIG1vZGVsOiByZXMKcmVzIDwtIGxtKFZvbHVtZX5HaXJ0aCwgZGF0YSA9IHRyZWVzKQoKIyBQbG90IHJlcwpwbG90KHJlcykKCiMgSW1wb3J0IGdnZm9ydGlmeSBhbmQgdXNlIGF1dG9wbG90KCkKbGlicmFyeShnZ2ZvcnRpZnkpCmF1dG9wbG90KHJlcywgbmNvbD0yKQpgYGAKCmdnZm9ydGlmeSAtIHRpbWUgc2VyaWVzClRpbWUgc2VyaWVzIG9iamVjdHMgKGNsYXNzIG10cyBvciB0cykgYWxzbyBoYXZlIHRoZWlyIG93biBtZXRob2RzIGZvciBwbG90KCkuIGdnZm9ydGlmeSBjYW4gYWxzbyB0YWtlIGFkdmFudGFnZSBvZiB0aGlzIGZ1bmN0aW9uYWxpdHkuCgpJbiB0aGUgd29ya3NwYWNlLCB5b3UnbGwgZmluZCB0aGUgdmFyaWFibGUgQ2FuYWRhIChpdCBjb21lcyBmcm9tIHRoZSB2YXJzIHBhY2thZ2UpOiBhbiBtdHMgY2xhc3Mgb2JqZWN0IHdpdGggZm91ciBzZXJpZXM6IHByb2QgaXMgYSBtZWFzdXJlIG9mIGxhYm91ciBwcm9kdWN0aXZpdHksIGUgaXMgZW1wbG95bWVudCwgVSBpcyB0aGUgdW5lbXBsb3ltZW50IHJhdGUsIGFuZCBydyB0aGUgcmVhbCB3YWdlLiBUaGV5IGFyZSBlYWNoIHBsb3R0ZWQgYXMgc2VwYXJhdGUgc2VyaWVzIGJ5IGRlZmF1bHQuCgpgYGB7cn0KIyBnZ2ZvcnRpZnkgYW5kIENhbmFkYSBhcmUgYXZhaWxhYmxlCmxpYnJhcnkodmFycykKIyBJbnNwZWN0IHN0cnVjdHVyZSBvZiBDYW5hZGEKc3RyKENhbmFkYSkKCiMgQ2FsbCBwbG90KCkgb24gQ2FuYWRhCnBsb3QoQ2FuYWRhKQoKIyBDYWxsIGF1dG9wbG90KCkgb24gQ2FuYWRhCmF1dG9wbG90KENhbmFkYSkKCmBgYAoKRGlzdGFuY2UgbWF0cmljZXMgYW5kIE11bHRpLURpbWVuc2lvbmFsIFNjYWxpbmcgKE1EUykKQXMgeW91IGNhbiBwcm9iYWJseSBpbWFnaW5lLCBkaXN0YW5jZSBtYXRyaWNlcyAoY2xhc3MgZGlzdCkgY29udGFpbiB0aGUgbWVhc3VyZWQgZGlzdGFuY2UgYmV0d2VlbiBhbGwgcGFpci13aXNlIGNvbWJpbmF0aW9ucyBvZiBtYW55IHBvaW50cy4gRm9yIGV4YW1wbGUsIHRoZSBldXJvZGlzdCBkYXRhc2V0IGNvbnRhaW5zIHRoZSBkaXN0YW5jZXMgYmV0d2VlbiBtYWpvciBFdXJvcGVhbiBjaXRpZXMuIGRpc3Qgb2JqZWN0cyBsZW5kIHRoZW1zZWx2ZXMgd2VsbCB0byBhdXRvcGxvdCgpLgoKVGhlIGNtZHNjYWxlKCkgZnVuY3Rpb24gZnJvbSB0aGUgc3RhdHMgcGFja2FnZSBwZXJmb3JtcyBDbGFzc2ljYWwgTXVsdGktRGltZW5zaW9uYWwgU2NhbGluZyBhbmQgcmV0dXJucyBwb2ludCBjb29kaW5hdGVzIGFzIGEgbWF0cml4LiBBbHRob3VnaCBhdXRvcGxvdCgpIHdpbGwgd29yayBvbiB0aGlzIG9iamVjdCwgaXQgd2lsbCBwcm9kdWNlIGEgaGVhdG1hcCwgYW5kIG5vdCBhIHNjYXR0ZXIgcGxvdC4gSG93ZXZlciwgaWYgZWl0aGVyIGVpZyA9IFRSVUUsIGFkZCA9IFRSVUUgb3IgeC5yZXQgPSBUUlVFIGlzIHNwZWNpZmllZCwgY21kc2NhbGUoKSB3aWxsIHJldHVybiBhIGxpc3QgaW5zdGVhZCBvZiBtYXRyaXguIEluIHRoZXNlIGNhc2VzLCB0aGUgbGlzdCBtZXRob2QgZm9yIGF1dG9wbG90KCkgaW4gdGhlIGdnZm9ydGlmeSBwYWNrYWdlIGNhbiBkZWFsIHdpdGggdGhlIG91dHB1dC4gU3BlY2lmaWNzIG9uIG11bHRpLWRpbWVuc2lvbmFsIHNjYWxpbmcgaXMgYmV5b25kIHRoZSBzY29wZSBvZiB0aGlzIGNvdXJzZSwgaG93ZXZlciBkZXRhaWxzIG9uIHRoZSBtZXRob2QgYW5kIHRoZXNlIGFyZ3VtZW50cyBjYW4gYmUgZm91bmQgaW4gdGhlIGhlbHAgcGFnZXMgP2NtZHNjYWxlLgoKYGBge3J9CiMgZ2dmb3J0aWZ5IGFuZCBldXJvZGlzdCBhcmUgYXZhaWxhYmxlCiMgQXV0b3Bsb3QgKyBnZ3Bsb3QyIHR3ZWFraW5nCmF1dG9wbG90KGV1cm9kaXN0KSArIAogIGNvb3JkX2ZpeGVkKCkKCiMgQXV0b3Bsb3Qgb2YgTURTCmF1dG9wbG90KGNtZHNjYWxlKGV1cm9kaXN0LCBlaWcgPSBUUlVFKSwgCiAgICAgICAgIGxhYmVsID0gVFJVRSwgCiAgICAgICAgIGxhYmVsLnNpemUgPSAzLCAKICAgICAgICAgc2l6ZSA9IDApCmBgYAoKUGxvdHRpbmcgSy1tZWFucyBjbHVzdGVyaW5nCmdnZm9ydGlmeSBhbHNvIHN1cHBvcnRzIHN0YXRzOjprbWVhbnMgY2xhc3Mgb2JqZWN0cy4gWW91IG11c3QgZXhwbGljaXRseSBwYXNzIHRoZSBvcmlnaW5hbCBkYXRhIHRvIHRoZSBhdXRvcGxvdCBmdW5jdGlvbiB2aWEgdGhlIGRhdGEgYXJndW1lbnQsIHNpbmNlIGttZWFucyBvYmplY3RzIGRvbid0IGNvbnRhaW4gdGhlIG9yaWdpbmFsIGRhdGEuIFRoZSByZXN1bHQgd2lsbCBiZSBhdXRvbWF0aWNhbGx5IGNvbG9yZWQgYWNjb3JkaW5nIHRvIGNsdXN0ZXIuCgpIZXJlLCB5b3UnbGwgdXNlIHRoZSBpcmlzIGRhdGFzZXQgYW5kIGp1c3QgbG9vayBhdCBLLW1lYW5zIGNsdXN0ZXJpbmcsIGFsdGhvdWdoIHRoaXMgd29ya3Mgb24gbWFueSBjbHVzdGVyaW5nIG1ldGhvZHMsIGluY2x1ZGluZyBjbHVzdGVyOjpjbGFyYSgpLCBjbHVzdGVyOjpmYW5ueSgpLCBjbHVzdGVyOjpwYW0oKSBhbmQgc3RhdHM6OnByY29tcCgpLiBVbmZvcnR1bmF0ZWx5IGEgZGlzY3Vzc2lvbiBvZiB0aGVzZSBjbHVzdGVyaW5nIG1ldGhvZHMgaXMgYmV5b25kIHRoZSBzY29wZSBvZiB0aGlzIGNvdXJzZS4KCmBgYHtyfQojIFBlcmZvcm0gY2x1c3RlcmluZwppcmlzX2sgPC0ga21lYW5zKGlyaXNbLTVdLCAzKQoKIyBBdXRvcGxvdDogY29sb3IgYWNjb3JkaW5nIHRvIGNsdXN0ZXIKYXV0b3Bsb3QoaXJpc19rLCBkYXRhID0gaXJpcywgZnJhbWUgPSBUUlVFKQoKIyBBdXRvcGxvdDogYWJvdmUsIHBsdXMgc2hhcGUgYWNjb3JkaW5nIHRvIHNwZWNpZXMKYXV0b3Bsb3QoaXJpc19rLCBkYXRhID0gaXJpcywgZnJhbWUgPSBUUlVFLCBzaGFwZT0nU3BlY2llcycpCmBgYAoKV29ya2luZyB3aXRoIG1hcHMgZnJvbSB0aGUgbWFwcyBwYWNrYWdlOiBVU0EKVGhlIGVhc2llc3Qgd2F5IHRvIG9idGFpbiBtYXAgcG9seWdvbnMgaXMgdGhyb3VnaCB0aGUgbWFwcyBwYWNrYWdlLiBVbmZvcnR1bmF0ZWx5IHRoZXJlIGFyZSBvbmx5IGEgZmV3IGxvY2F0aW9ucyBhdmFpbGFibGUsIGJ1dCBpZiB5b3VyIHJlZ2lvbiBvZiBpbnRlcmVzdCBpcyBpbmNsdWRlZCB0aGV5IGFyZSBleHRyZW1lbHkgY29udmVuaWVudC4KClRoZSBhdmFpbGFibGUgbWFwcyBvZiBwb2xpdGljYWwgYm91bmRhcmllcyBhcmU6CgpHbG9iYWw6IHdvcmxkLCB3b3JsZDIKQ291bnRyeTogZnJhbmNlLCBpdGFseSwgbnosIHVzYQpVU0E6IGNvdW50eSwgc3RhdGUKVGhlIG1hcHMgY2FuIGJlIGFjY2Vzc2VkIHZpYSBnZ3Bsb3QyOjptYXBfZGF0YSgpLCB3aGljaCBjb252ZXJ0cyB0aGUgbWFwIGludG8gYSBkYXRhIGZyYW1lIGNvbnRhaW5pbmcgdGhlIHZhcmlhYmxlcyBsb25nIGFuZCBsYXQuIFRvIGRyYXcgdGhlIG1hcCwgeW91IG5lZWQgdG8gdXNlIGdlb21fcG9seWdvbigpIHdoaWNoIHdpbGwgY29ubmVjdCB0aGUgcG9pbnRzIG9mIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgZm9yIHlvdS4KCmBgYHtyfQpsaWJyYXJ5KG1hcHMpCiMgbWFwcywgZ2dwbG90MiwgYW5kIGdnbWFwIGFyZSBwcmUtbG9hZGVkCiMgVXNlIG1hcF9kYXRhKCkgdG8gY3JlYXRlIHVzYSBhbmQgaW5zcGVjdAp1c2EgPC0gbWFwX2RhdGEoInVzYSIpCnN0cih1c2EpCgojIEJ1aWxkIHRoZSBtYXAKZ2dwbG90KHVzYSwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSkgKwogIGdlb21fcG9seWdvbigpICsKICBjb29yZF9tYXAoKSArCiAgdGhlbWVfbm90aGluZygpCmBgYAoKVGhlIHBvcHVsYXRpb24gcHlyYW1pZApBbmltYXRpb25zIGFyZSBwYXJ0aWN1bGFybHkgdXNlZnVsIGZvciB0ZW1wb3JhbCBvciBnZW9zcGF0aWFsIGRhdGEsIGFuZCB0aGV5IGFyZSBzdXJwcmlzaW5nbHkgZWFzeSB0byBtYWtlISBIZXJlLCB5b3Ugc2ltcGx5IGxvb3Agb3ZlciB0aGUgdGltZSB2YXJpYWJsZSBpbiB5b3VyIGRhdGFzZXQsIGNvbXBvc2luZyBhIG5ldyBwbG90IGZvciBlYWNoIHN1YnNldCBpbiB0aGUgZGF0YS4gVGhlc2UgaW5kaXZpZHVhbCBpbWFnZXMgYXJlIHRoZW4gY2F0YWxvZ2VkIGluIGFuIGFuaW1hdGVkIEdJRiBmaWxlLgoKVG8gc2hvdyB0aGlzIHlvdSdsbCB1c2UgYSBncmVhdCBhbmltYXRlZCBwb3B1bGF0aW9uIHB5cmFtaWQgdGhhdCB3YXMgcHJlc2VudGVkIG9uIHRoZSBSZXZvbHV0aW9ucyBibG9nLiBUaGVyZSBhcmUgbWFueSBtb3JlIGFkanVzdG1lbnRzIHlvdSBjb3VsZCBoYXZlIG1hZGUgdG8gdGhlIHBsb3QsIGJ1dCB3ZSdsbCBqdXN0IG1ha2UgYSBiYXJlYm9uZXMgdmVyc2lvbiBoZXJlLgoKYGBge3J9CmphcGFuIDwtIHJlYWQudGFibGUoImphcGFuUE9QLnR4dCIsIGhlYWRlcj1UUlVFKQpoZWFkKGphcGFuKQpgYGAKCmBgYHtyfQojIEluc3BlY3Qgc3RydWN0dXJlIG9mIGphcGFuCnN0cihqYXBhbikKbGlicmFyeShhbmltYXRpb24pCiMgRmluaXNoIHRoZSBjb2RlIGluc2lkZSBzYXZlR0lGCnNhdmVHSUYoewoKICAjIExvb3AgdGhyb3VnaCBhbGwgdGltZSBwb2ludHMKICBmb3IgKGkgaW4gdW5pcXVlKGphcGFuJHRpbWUpKSB7CgogICAgIyBTdWJzZXQgamFwYW46IGRhdGEKICAgIGRhdGEgPC0gc3Vic2V0KGphcGFuLCB0aW1lID09IGkpCgogICAgIyBGaW5pc2ggdGhlIGdncGxvdCBjb21tYW5kCiAgICBwIDwtIGdncGxvdChkYXRhLCBhZXMoeCA9IEFHRSwgeSA9IFBPUCwgZmlsbCA9IFNFWCwgd2lkdGggPSAxKSkgKwogICAgICBjb29yZF9mbGlwKCkgKwogICAgICBnZW9tX2JhcihkYXRhID0gZGF0YVtkYXRhJFNFWCA9PSAiRmVtYWxlIixdLCBzdGF0ID0gImlkZW50aXR5IikgKwogICAgICBnZW9tX2JhcihkYXRhID0gZGF0YVtkYXRhJFNFWCA9PSAiTWFsZSIsXSwgc3RhdCA9ICJpZGVudGl0eSIpICsKICAgICAgZ2d0aXRsZShpKQoKICAgIHByaW50KHApCgogIH0KCn0sIG1vdmllLm5hbWUgPSAicHlyYW1pZC5naWYiLCBpbnRlcnZhbCA9IDAuMSkKYGBgCgo=